{"record":{"id":"0c8567b07d36cfda","repo":"henrygd/beszel","slug":"hash-mismatch-got-s-want-s","errorCode":null,"errorMessage":"hash mismatch: got %s want %s","messagePattern":"hash mismatch: got (.+?) want (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"agent/tools/fetchsmartctl/main.go","lineNumber":109,"sourceCode":"\tif hasher != nil {\n\t\tmw = io.MultiWriter(f, hasher)\n\t}\n\tif _, err := io.Copy(mw, resp.Body); err != nil {\n\t\tf.Close()\n\t\tos.Remove(tmp)\n\t\treturn fmt.Errorf(\"write tmp: %w\", err)\n\t}\n\tif err := f.Close(); err != nil {\n\t\tos.Remove(tmp)\n\t\treturn fmt.Errorf(\"close tmp: %w\", err)\n\t}\n\n\tif hasher != nil && shaHex != \"\" {\n\t\tcleanSha := strings.ToLower(strings.ReplaceAll(strings.TrimSpace(shaHex), \" \", \"\"))\n\t\tgot := strings.ToLower(hex.EncodeToString(hasher.Sum(nil)))\n\t\tif got != cleanSha {\n\t\t\tos.Remove(tmp)\n\t\t\treturn fmt.Errorf(\"hash mismatch: got %s want %s\", got, cleanSha)\n\t\t}\n\t}\n\n\t// Make executable and move into place\n\tif err := os.Chmod(tmp, 0o755); err != nil {\n\t\tos.Remove(tmp)\n\t\treturn fmt.Errorf(\"chmod: %w\", err)\n\t}\n\tif err := os.Rename(tmp, dest); err != nil {\n\t\tos.Remove(tmp)\n\t\treturn fmt.Errorf(\"rename: %w\", err)\n\t}\n\n\tfmt.Println(\"smartctl.exe downloaded to\", dest)\n\treturn nil\n}\n\nfunc fatalf(format string, a ...any) {","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/henrygd/beszel/blob/b38fb7dafa60812cc22e6a84ce313e94f1ce0a32/agent/tools/fetchsmartctl/main.go#L91-L127","documentation":"downloadFile in fetchsmartctl verifies the downloaded binary against an expected SHA hash (normalized to lowercase, whitespace-stripped). After hashing the downloaded temp file, it compares the computed hex digest to the expected value and fails the download if they differ, deleting the temp file. This protects against corrupted or tampered binaries.","triggerScenarios":"The expected sha256 string (shaHex) does not match the actual hash of the bytes fetched from the remote URL: wrong/pinned hash for a different smartctl.exe release, truncated or proxied/modified download, or a hash string referring to a different file after cleanup.","commonSituations":"Upstream smartmontools updated the binary but the script still pins the old hash; corporate proxy or CDN serving different content; copy-paste error in the embedded hash (wrong algorithm, e.g. SHA-1 instead of SHA-256, or hash of a zip instead of the exe).","solutions":["Check upstream release notes for the new smartctl.exe version and update the pinned hash constant to the official SHA-256 for that exact file.","Re-download manually and compute `sha256sum smartctl.exe` to confirm what the remote is actually serving; if it differs from upstream, suspect a proxy/mirror serving modified content.","Verify the pinned hash is the SHA-256 hex digest (64 hex chars) of the binary itself, not of an archive or signature file.","If the download was truncated, retry or fix the network, then re-run fetchsmartctl."],"exampleFix":"// before\nshaHex = \"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855\" // stale pin\n// after\n// $ sha256sum smartctl.exe  (hash of the release actually being fetched)\nshaHex = \"9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08\"","handlingStrategy":"validation","validationCode":"// verify the pinned hash matches the exact artifact you serve before calling downloadFile\nsum := sha256.Sum256(expectedArtifactBytes)\nif !strings.EqualFold(hex.EncodeToString(sum[:]), expectedShaHex) {\n\treturn errors.New(\"pinned hash does not match known artifact; update it\")\n}","typeGuard":null,"tryCatchPattern":"if err := downloadFile(url, dest, sha256Hex); err != nil {\n\tif strings.HasPrefix(err.Error(), \"hash mismatch\") {\n\t\tlog.Printf(\"integrity check failed: %v\", err) // refresh hash or abort\n\t} else {\n\t\treturn err\n\t}\n}","preventionTips":["Pin hash and release version together; update both in the same commit.","Hash the exact artifact downloaded (not the zip/signature).","Never bypass verification; log got/want on mismatch.","Watch for proxies/CDNs altering binary content."],"tags":["go","hash-mismatch","checksum-verification","security"],"backgroundTag":"checksum-hash-mismatch","analyzedSha":"b38fb7dafa60812cc22e6a84ce313e94f1ce0a32","analyzedAt":"2026-08-31T15:10:10.149Z","schemaVersion":2},"datasetVersion":"2026-08-31T19:17:28.585Z"}