{"record":{"id":"485b38f7783a879f","repo":"tonhowtf/omniget","slug":"hash-nao-confere-esperado-obtido-download-descartado","errorCode":null,"errorMessage":"{}: hash nao confere — esperado {}, obtido {}. Download descartado.","messagePattern":"(.+?): hash nao confere — esperado (.+?), obtido (.+?)\\. Download descartado\\.","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"src-tauri/omniget-core/src/core/dependencies.rs","lineNumber":71,"sourceCode":"    /// Arquivo `.sha256sum` de asset único (o Deno publica um por asset).\n    pub fn parse_single_sha256(text: &str) -> Option<String> {\n        let first = text.split_whitespace().next()?;\n        is_sha256(first).then(|| first.to_lowercase())\n    }\n\n    /// `digest` da API de releases do GitHub, no formato `sha256:<hex>`.\n    pub fn parse_github_digest(digest: &str) -> Option<String> {\n        let hex = digest.trim().strip_prefix(\"sha256:\")?;\n        is_sha256(hex).then(|| hex.to_lowercase())\n    }\n\n    pub fn verify_sha256(bytes: &[u8], expected: &str, label: &str) -> anyhow::Result<()> {\n        let actual = sha256_hex(bytes);\n        if actual == expected.to_lowercase() {\n            tracing::info!(\"[integrity] {} verificado (sha256 confere)\", label);\n            return Ok(());\n        }\n        Err(anyhow!(\n            \"{}: hash nao confere — esperado {}, obtido {}. Download descartado.\",\n            label,\n            expected,\n            actual\n        ))\n    }\n\n    /// Busca o hash esperado num arquivo de sums remoto. `Err` quando a origem\n    /// publica sums mas não conseguimos obtê-los — o chamador deve abortar.\n    pub async fn expected_from_sums_url(\n        client: &reqwest::Client,\n        sums_url: &str,\n        asset: &str,\n    ) -> anyhow::Result<String> {\n        let response = client\n            .get(sums_url)\n            .send()\n            .await","sourceCodeStart":53,"sourceCodeEnd":89,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/omniget-core/src/core/dependencies.rs#L53-L89","documentation":"verify_sha256 computes the SHA-256 of the downloaded bytes and compares it to the expected lowercase hex digest. On mismatch it discards the download and throws an error naming the label, expected hash, and actual hash. This integrity guard prevents running tampered or corrupt binaries.","triggerScenarios":"Calling verify_sha256 with bytes whose actual SHA-256 does not equal the expected hex string (case-insensitively) — e.g. the binary published upstream changed after the expected hash was pinned, the download was truncated/corrupted, or a proxy/CDN served different content.","commonSituations":"Upstream released a new build without updating the pinned hash; interrupted or proxied download corrupting bytes; MITM or supply-chain tampering (the exact scenario this check defends against); wrong hash string copied from release notes.","solutions":["Compare expected vs actual hashes in the message: if the upstream release changed, update the pinned expected hash from the official release source.","Re-download the file and verify again — transient corruption/truncation is fixed by a fresh download.","Confirm the hash comes from a trusted channel (official release notes over HTTPS, not a mirror).","If the mismatch persists with a trusted hash, treat the source as compromised: do not execute the binary and investigate the network path."],"exampleFix":"// before\nlet bytes = download(url).await?;\nverify_sha256(&bytes, EXPECTED_HASH, \"recusa-binario\")?;\n\n// after\nlet bytes = download(url).await?;\nif let Err(e) = verify_sha256(&bytes, EXPECTED_HASH, \"recusa-binario\") {\n    tracing::warn!(\"hash mismatch, re-downloading once: {}\", e);\n    let bytes = download(url).await?;\n    verify_sha256(&bytes, EXPECTED_HASH, \"recusa-binario\")?;\n}","handlingStrategy":"validation","validationCode":"fn sha256_matches(bytes: &[u8], expected: &str) -> bool {\n    let actual = sha256_hex(bytes);\n    actual == expected.to_lowercase()\n}\n// call before executing/using any downloaded binary\nif !sha256_matches(&bytes, EXPECTED_HASH) {\n    eprintln!(\"Integrity check failed — discard download, do not execute\");\n}","typeGuard":"fn is_valid_hex_sha256(s: &str) -> bool {\n    s.len() == 64 && s.chars().all(|c| c.is_ascii_hexdigit())\n}","tryCatchPattern":"match verify_sha256(&bytes, expected, label) {\n    Err(e) => {\n        // Never fall back to executing unverified bytes\n        tracing::error!(\"discarding download: {}\", e);\n        std::fs::remove_file(temp_path)?;\n        return Err(e);\n    }\n    Ok(()) => { /* safe to proceed */ }\n}","preventionTips":["Always pin hashes from official HTTPS release channels","Re-download once on mismatch to rule out transient corruption","Never execute binaries that fail the integrity check","Update pinned hashes promptly when upstream releases new builds","Normalize expected hashes to lowercase before comparing"],"tags":["integrity","sha256","checksum","security","supply-chain"],"backgroundTag":"checksum-mismatch","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}