{"record":{"id":"7af53b0aeb7434e0","repo":"tonhowtf/omniget","slug":"veio-sem-digest-da-api-do-github-download-descartado","errorCode":null,"errorMessage":"{} veio sem digest da API do GitHub; download descartado","messagePattern":"(.+?) veio sem digest da API do GitHub; download descartado","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/omniget-core/src/core/spicetify.rs","lineNumber":432,"sourceCode":"}\n\nasync fn download_verified(\n    client: &reqwest::Client,\n    asset: &ReleaseAsset,\n) -> anyhow::Result<Vec<u8>> {\n    let response = client.get(&asset.url).send().await?;\n    if !response.status().is_success() {\n        return Err(anyhow!(\n            \"download de {} falhou: HTTP {}\",\n            asset.name,\n            response.status()\n        ));\n    }\n    let bytes = response.bytes().await?.to_vec();\n    // O GitHub publica o digest de todo asset; sem ele algo está errado na\n    // resposta, e o binário vai ser executado. Fail-closed.\n    let expected = asset.digest.as_deref().ok_or_else(|| {\n        anyhow!(\n            \"{} veio sem digest da API do GitHub; download descartado\",\n            asset.name\n        )\n    })?;\n    integrity::verify_sha256(&bytes, expected, &asset.name)?;\n    Ok(bytes)\n}\n\n/// Sufixo do asset do CLI para este sistema. Linux só tem amd64 no release.\nfn cli_asset_suffix() -> anyhow::Result<&'static str> {\n    Ok(if cfg!(target_os = \"windows\") {\n        if cfg!(target_arch = \"aarch64\") {\n            \"windows-arm64.zip\"\n        } else if cfg!(target_pointer_width = \"32\") {\n            \"windows-x32.zip\"\n        } else {\n            \"windows-x64.zip\"\n        }","sourceCodeStart":414,"sourceCodeEnd":450,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/spicetify.rs#L414-L450","documentation":"GitHub's API publishes a sha256 `digest` for every release asset. `download_verified` refuses to keep a downloaded binary if the digest is absent from the API response — a deliberate fail-closed policy since the bytes will be executed later. This protects against tampered or malformed API responses.","triggerScenarios":"`download_verified` receiving a `ReleaseAsset` whose `digest` field is None — GitHub omitted the `digest` field (older API behavior, proxies stripping it, or code that failed to parse it via `integrity::parse_github_digest`).","commonSituations":"Corporate proxy or mirror stripping JSON fields; GitHub temporarily omitting digests in an API change; a custom GitHub Enterprise server without digest support; parsing bug for the digest format.","solutions":["Inspect the raw API JSON for the asset and confirm a `sha256-...` digest field exists","Remove/adjust proxies or mirrors that strip fields, or query api.github.com directly","Update to a version where `parse_github_digest` handles the current digest format","If unavoidable (e.g. GHE without digests), provide a verified checksum out-of-band — do not bypass the check"],"exampleFix":"// before\nlet digest = asset.digest.as_deref().ok_or_else(|| anyhow!(\"... sem digest ...\"))?;\n// after\nlet digest = match asset.digest.as_deref() {\n    Some(d) => d,\n    None => fetch_digest_from_checksums_file(&client, &asset).await? // out-of-band verified source\n};","handlingStrategy":"validation","validationCode":"let json: serde_json::Value = client.get(asset_api_url).send().await?.json().await?;\nif json[\"digest\"].as_str().is_none() {\n    eprintln!(\"API response missing digest — resolve proxy/GHE issue before downloading\");\n}","typeGuard":null,"tryCatchPattern":"match download_verified(&client, &asset).await {\n    Err(e) if e.to_string().contains(\"sem digest\") => {\n        // do NOT bypass; obtain checksum from a second trusted source or abort\n        obtain_checksum_out_of_band(&asset).map(|d| verify_and_use(bytes, d))\n    }\n    other => other,\n}","preventionTips":["Never bypass the fail-closed digest check for code that will be executed","Query api.github.com directly; beware proxies/enterprise mirrors that strip JSON fields","Keep `integrity::parse_github_digest` updated to the current `sha256:...` format","Prefer integrity-verified downloads over raw URL fetches"],"tags":["security","integrity","github-api"],"backgroundTag":"checksum-mismatch","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}