{"record":{"id":"8293088b6ac4a185","repo":"ruvnet/ruflo","slug":"sha256sums-has-no-entry-for-input-assetfilename","errorCode":null,"errorMessage":"SHA256SUMS has no entry for ${input.assetFilename}","messagePattern":"SHA256SUMS has no entry for (.+?)","errorType":"exception","errorClass":"ReleaseVerificationError","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/proxy/verify.ts","lineNumber":85,"sourceCode":"export interface VerifyReleaseResult {\n  sha256: string;\n}\n\n/**\n * Full verification: signature over SHA256SUMS, then the asset's own hash\n * against the matching line. Throws `ReleaseVerificationError` on ANY\n * failure — there is no partial-trust outcome, matching ADR-307's \"refuses\n * on any mismatch\" requirement.\n */\nexport function verifyRelease(input: VerifyReleaseInput): VerifyReleaseResult {\n  if (!verifySha256SumsSignature(input.sumsBytes, input.sigBase64, input.pubkeyPem)) {\n    throw new ReleaseVerificationError('SHA256SUMS.sig failed Ed25519 verification — refusing to install');\n  }\n\n  const sums = parseSha256Sums(input.sumsBytes.toString('utf-8'));\n  const expected = sums[input.assetFilename];\n  if (!expected) {\n    throw new ReleaseVerificationError(`SHA256SUMS has no entry for ${input.assetFilename}`);\n  }\n\n  const actual = sha256Hex(input.assetBytes);\n  if (actual !== expected) {\n    throw new ReleaseVerificationError(\n      `sha256 mismatch for ${input.assetFilename}: expected ${expected.slice(0, 12)}…, got ${actual.slice(0, 12)}…`,\n    );\n  }\n\n  return { sha256: actual };\n}\n","sourceCodeStart":67,"sourceCodeEnd":97,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/proxy/verify.ts#L67-L97","documentation":"Thrown by verifyRelease() after the Ed25519 signature on SHA256SUMS has already passed, but the requested assetFilename has no matching line in the parsed SHA256SUMS file. This is the second of three all-or-nothing gates in ADR-307's release verification: signature first, then entry presence, then hash match. The signer is trusted (sig passed), so this specifically means the filename you asked to verify is not covered by that signed manifest.","triggerScenarios":"Calling verifyRelease({ assetFilename: 'ruflo-darwin-arm64', ... }) when SHA256SUMS only lists 'ruflo-linux-x64'; passing a filename with a different path prefix/suffix (e.g. 'bin/ruflo' vs 'ruflo'); using a SHA256SUMS from release v0.1.0 to verify an asset from v0.2.0; passing a Windows-style backslash filename while the manifest uses forward slashes; passing the directory name instead of the asset basename.","commonSituations":"CI pipeline fetches the SHA256SUMS for the wrong release tag; the proxy downloads assets and sums from two different GitHub release pages; a release publisher forgot to include the new platform's binary in SHA256SUMS; filename was normalized (case-folded, trailing whitespace) before lookup but the manifest has the raw form.","solutions":["Print parseSha256Sums(input.sumsBytes.toString('utf-8')) and confirm your assetFilename matches a key exactly (case-sensitive, same separators).","Verify you fetched SHA256SUMS and the asset from the SAME release tag — mismatched versions is the most common cause.","If the asset uses a path prefix in the manifest (e.g. 'dist/rufvo-linux-x64'), pass the full key as it appears in SHA256SUMS, not just the basename.","If the manifest genuinely lacks the entry, treat it as a release-packaging defect: refuse to install and report upstream — do not loosen the check."],"exampleFix":"// before — basename only, manifest lists prefixed path\nverifyRelease({ assetFilename: 'ruflo-linux-x64', sumsBytes, sigBase64, assetBytes });\n\n// after — match the exact key present in SHA256SUMS\nconst sums = parseSha256Sums(sumsBytes.toString('utf-8'));\nconst key = Object.keys(sums).find(k => k.endsWith('ruflo-linux-x64'));\nif (!key) throw new Error('asset not in manifest — wrong release?');\nverifyRelease({ assetFilename: key, sumsBytes, sigBase64, assetBytes });","handlingStrategy":"validation","validationCode":"import { parseSha256Sums } from './verify';\n\nfunction findAssetKey(sumsBytes: Buffer, requestedAsset: string): string | null {\n  const sums = parseSha256Sums(sumsBytes.toString('utf-8'));\n  if (sums[requestedAsset]) return requestedAsset;            // exact match\n  // tolerate path/separator differences\n  const norm = (s: string) => s.replace(/\\\\/g, '/').toLowerCase();\n  const want = norm(requestedAsset);\n  for (const key of Object.keys(sums)) {\n    if (norm(key) === want || norm(key).endsWith('/' + want)) return key;\n  }\n  return null;\n}\n\n// before calling verifyRelease:\nconst key = findAssetKey(sumsBytes, assetFilename);\nif (!key) throw new Error(`asset '${assetFilename}' not in SHA256SUMS; available: ${Object.keys(parseSha256Sums(sumsBytes.toString())).slice(0,5).join(', ')}...`);","typeGuard":null,"tryCatchPattern":"try {\n  verifyRelease({ assetFilename: key, sumsBytes, sigBase64, assetBytes });\n} catch (e) {\n  if (e instanceof ReleaseVerificationError && e.message.startsWith('SHA256SUMS has no entry')) {\n    // filename mismatch — do NOT retry with a different filename blindly;\n    // surface the manifest keys so the operator can pick the right one.\n    throw new Error(`${e.message}. Manifest entries: ${Object.keys(parseSha256Sums(sumsBytes.toString('utf-8'))).join(', ')}`);\n  }\n  throw e;\n}","preventionTips":["Always fetch SHA256SUMS and the asset from the same release tag in the same HTTP session.","Log Object.keys(parseSha256Sums(sumsBytes)) once at startup so missing-asset errors are debuggable.","Treat the asset filename as opaque — pass exactly the key that appears in the manifest.","Never auto-retry by stripping path prefixes; a missing entry is a release-packaging signal, not a normalization issue."],"tags":["security","release-verification","supply-chain","checksums","adr-307"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T23:17:12.415Z"}