{"record":{"id":"99f794da59ef3ddc","repo":"NousResearch/hermes-agent","slug":"checksum-mismatch-for-asset-name-expected-expe","errorCode":null,"errorMessage":"Checksum mismatch for {asset_name}: expected {expected}, got {actual}","messagePattern":"Checksum mismatch for (.+?): expected (.+?), got (.+?)","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"critical","filePath":"agent/proxy_sources/iron_proxy.py","lineNumber":505,"sourceCode":"        checksum_path = tmp / _IRON_PROXY_CHECKSUM_NAME\n\n        logger.info(\"Downloading %s\", asset_url)\n        _http_download(asset_url, archive_path)\n        _http_download(checksum_url, checksum_path)\n\n        # Defense-in-depth (maxpetrusenko P1): verify the GPG signature of\n        # checksums.txt before trusting it. The archive download honors ambient\n        # proxy env (urllib), so a compromised channel could serve a matching\n        # binary + checksums pair; the detached signature + pinned public key\n        # close that release-channel tamper gap. Best-effort: if gpg or the\n        # signature assets aren't available we log and fall back to the SHA-256\n        # check alone rather than hard-failing offline installs.\n        _verify_checksums_signature(tmp, checksum_path)\n\n        expected = _expected_sha256(checksum_path, asset_name)\n        actual = _sha256_file(archive_path)\n        if expected.lower() != actual.lower():\n            raise RuntimeError(\n                f\"Checksum mismatch for {asset_name}: \"\n                f\"expected {expected}, got {actual}\"\n            )\n\n        with tarfile.open(archive_path, \"r:gz\") as tf:\n            member = _pick_tar_member(tf, _platform_binary_name())\n            # PEP 706 data filter — strips ownership/mode replay (we set\n            # chmod explicitly below) AND rejects symlink/hardlink members\n            # that escape the extraction dir.  Required on 3.12+ to silence\n            # the deprecation warning and on 3.14+ to opt into the\n            # tarbomb-rejecting default.\n            try:\n                tf.extract(member, tmp, filter=\"data\")  # noqa: S202\n            except TypeError:\n                # Python < 3.12 — filter kw didn't exist yet; the\n                # _pick_tar_member sanitization already rejects path\n                # traversal so this is acceptable.\n                tf.extract(member, tmp)  # noqa: S202","sourceCodeStart":487,"sourceCodeEnd":523,"githubUrl":"https://github.com/NousResearch/hermes-agent/blob/c896c09c42910c584c4c7d2325b58c14713ea42c/agent/proxy_sources/iron_proxy.py#L487-L523","documentation":"After downloading the iron-proxy release archive, its SHA-256 is compared against the entry in the release's checksums.txt (which is itself GPG-verified best-effort). A mismatch means the bytes on disk are not the bytes the release signed — either a corrupted/truncated download (proxies and flaky links are the usual cause) or genuine release-channel tampering. The installer refuses to extract and install the binary.","triggerScenarios":"find_iron_proxy(install_if_missing=True) / `hermes egress install` completes the archive download, but _sha256_file(archive_path) != _expected_sha256(checksum_path, asset_name). Typical causes: a corporate MITM proxy rewriting the GitHub asset download, a partially-written file from a dropped connection, or a partially-written file left in the temp dir.","commonSituations":"Installing behind an intercepting corporate proxy or on flaky Wi-Fi; a cached/partial download being reused; a genuinely compromised mirror (rare, but this check plus the GPG layer exists precisely for that).","solutions":["Delete any partial download/temp state under the proxy state dir and retry `hermes egress install` on a stable connection.","Check for intercepting proxies: verify `curl -L <asset-url> | sha256sum` matches the published checksums.txt entry from the same release page.","If the mismatch reproduces on a clean network, treat it as a possible release-channel compromise — do NOT bypass the check; verify the checksums.txt GPG signature manually against the project's pinned key and report it."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    find_iron_proxy(install_if_missing=True)\nexcept RuntimeError as e:\n    if \"Checksum mismatch\" in str(e):\n        # do NOT auto-retry in a loop: clear partial downloads once, retry once,\n        # then surface to a human — this can indicate tampering\n        raise","preventionTips":["Treat any reproducible checksum mismatch as a security incident, not a flake — verify the release manually before installing.","Install from a trusted network without TLS-intercepting proxies.","Pin the iron-proxy version and install once at image-build time instead of lazily at runtime."],"tags":["security","checksum","download","iron-proxy"],"backgroundTag":null,"analyzedSha":"c896c09c42910c584c4c7d2325b58c14713ea42c","analyzedAt":"2026-08-14T17:18:01.089Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}