{"record":{"id":"25506d111b9e0a9d","repo":"mihomo-party-org/clash-party","slug":"github-release-does-not-provide-a-sha-256-digest-f","errorCode":null,"errorMessage":"GitHub Release does not provide a SHA-256 digest for \"${file}\"","messagePattern":"GitHub Release does not provide a SHA-256 digest for \"(.+?)\"","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"critical","filePath":"src/main/resolve/autoUpdater.ts","lineNumber":94,"sourceCode":"  proxy: UpdaterProxy\n): Promise<string> {\n  const releaseTag = encodeURIComponent(`v${version}`)\n  const res = await chromeRequest.get<GitHubRelease>(\n    `https://api.github.com/repos/mihomo-party-org/mihomo-party/releases/tags/${releaseTag}`,\n    {\n      headers: {\n        Accept: 'application/vnd.github+json',\n        'X-GitHub-Api-Version': '2022-11-28'\n      },\n      proxy,\n      timeout: 5000,\n      responseType: 'json'\n    }\n  )\n  const digest = res.data.assets?.find((asset) => asset.name === file)?.digest\n  const match = digest?.match(/^sha256:([a-f\\d]{64})$/i)\n  if (!match) {\n    throw new Error(`GitHub Release does not provide a SHA-256 digest for \"${file}\"`)\n  }\n  return match[1].toLowerCase()\n}\n\nexport async function checkUpdate(): Promise<IAppVersion | undefined> {\n  const [{ 'mixed-port': mixedPort = DEFAULT_MIHOMO_PORTS.mixed }, { githubProxy = '' }] =\n    await Promise.all([getControledMihomoConfig(), getAppConfig()])\n  const githubUrl =\n    'https://github.com/mihomo-party-org/mihomo-party/releases/latest/download/latest.yml'\n  const res = await tryDownload(buildDownloadUrls(githubUrl, githubProxy), {\n    headers: { 'Content-Type': 'application/octet-stream' },\n    proxy: updaterProxy(mixedPort),\n    responseType: 'text'\n  })\n  const latest = parse(res.data as string) as IAppVersion\n  // 错误页也能被 YAML 解析成对象（如 `404: Not Found`），不校验会让 compareVersions 崩在 undefined.replace\n  if (!latest || typeof latest.version !== 'string') {\n    throw new Error('Invalid latest.yml from update source')","sourceCodeStart":76,"sourceCodeEnd":112,"githubUrl":"https://github.com/mihomo-party-org/clash-party/blob/911e090537acdf7c50bee1c3aebecc2ef119a8b5/src/main/resolve/autoUpdater.ts#L76-L112","documentation":"getGitHubAssetSha256 (src/main/resolve/autoUpdater.ts:94) fetches the GitHub Release metadata and extracts the asset's `digest` field, expecting the format `sha256:<64 hex chars>`. If the digest is absent or malformed (no regex match), it throws because the updater refuses to install a binary whose integrity cannot be verified. Note: GitHub only populates the digest field on newer releases/API responses, so older or third-party-mirrored releases commonly lack it.","triggerScenarios":"Calling installUpdate when the GitHub Release for the target file has no digest on the asset, a digest in an unexpected format (not sha256:...), or the JSON response lacks the asset entry (name mismatch between latest.yml file and actual release asset).","commonSituations":"Old releases published before GitHub added sha256 digests; proxied/mirror sources strip or alter release metadata; asset name in latest.yml does not exactly match the release asset name so find() returns undefined; GitHub API returning partial/cached JSON.","solutions":["Verify the GitHub Release assets actually include a `digest` field of form sha256:<64 hex> (curl the release API for the tag)","Ensure the `file` name passed in exactly matches the release asset name (case-sensitive)","Update to a release published after GitHub added digest support, or republish the release assets so GitHub generates digests","If using a proxy for the release API call, fetch metadata directly from api.github.com so the digest field is not stripped"],"exampleFix":"// before\ngetGitHubAssetSha256('AppSetup-0.9.0-old.exe') // release predates digests\n// after — verify before calling\nconst assets = await getReleaseAssets(tag)\nif (!assets.find(a => a.digest?.startsWith('sha256:'))) {\n  throw new Error('release lacks sha256 digest; publish new assets')\n}","handlingStrategy":"validation","validationCode":"const res = await octokit.rest.repos.getReleaseByTag({ owner, repo, tag })\nconst digest = res.data.assets.find(a => a.name === file)?.digest\nconst ok = typeof digest === 'string' && /^sha256:[a-f\\d]{64}$/i.test(digest)\nif (!ok) throw new Error(`asset ${file} lacks sha256 digest; cannot verify`)","typeGuard":"function hasSha256Digest(asset: { name: string; digest?: string }): asset is { name: string; digest: string } {\n  return /^sha256:[a-f\\d]{64}$/i.test(asset.digest ?? '')\n}","tryCatchPattern":"try {\n  const sha = await getGitHubAssetSha256(file)\n} catch (e) {\n  if (e instanceof Error && e.message.includes('SHA-256 digest')) {\n    // refuse install; prompt user to republish release or fetch checksum from elsewhere\n    safeShowErrorBox('updater.integrityUnavailable', e.message)\n  }\n}","preventionTips":["Only auto-update from releases published after GitHub digest support (mid-2024+); verify via the release API","Compare asset names exactly (case-sensitive) between latest.yml and release assets","Fetch release metadata directly from api.github.com rather than through stripping proxies","Treat missing digests as a hard failure — never install unverifiable binaries (supply-chain guard)"],"tags":["updater","integrity","sha256","github-api","supply-chain"],"backgroundTag":"missing-checksum-digest","analyzedSha":"911e090537acdf7c50bee1c3aebecc2ef119a8b5","analyzedAt":"2026-08-30T13:00:49.174Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}