{"record":{"id":"53239fb65f1d09d6","repo":"dotnet/aspnetcore","slug":"sha256-mismatch-for-path-expected-packages-sha","errorCode":null,"errorMessage":"SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}","messagePattern":"SHA256 mismatch for (.+?): expected (.+?), got (.+?)","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"critical","filePath":"eng/common/cross/install-debs.py","lineNumber":105,"sourceCode":"    \"\"\"Fetch and decompress the Packages.gz file.\"\"\"\n\n    path = f\"{component}/binary-{arch}/Packages.gz\"\n    url = f\"{mirror}/dists/{suite}/{path}\"\n\n    async with session.get(url) as response:\n        if response.status == 200:\n            compressed_data = await response.read()\n            decompressed_data = gzip.decompress(compressed_data).decode('utf-8')\n            print(f\"Downloaded index: {url}\")\n\n            if check_sig:\n                # Verify the package index against the sha256 recorded in the Release file\n                release_file_content = await fetch_release_file(session, mirror, suite, keyring)\n                packages_sha = parse_release_file(release_file_content, path)\n\n                sha256 = hashlib.sha256(compressed_data).hexdigest()\n                if sha256 != packages_sha:\n                    raise Exception(f\"SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}\")\n                print(f\"Checksum verified for {path}\")\n\n            return decompressed_data\n        else:\n            print(f\"Skipped index: {url} (doesn't exist)\")\n            return None\n\nasync def fetch_release_file(session, mirror, suite, keyring):\n    \"\"\"Fetch Release and Release.gpg files and verify the signature.\"\"\"\n\n    release_url = f\"{mirror}/dists/{suite}/Release\"\n    release_gpg_url = f\"{mirror}/dists/{suite}/Release.gpg\"\n\n    with tempfile.NamedTemporaryFile() as release_file, tempfile.NamedTemporaryFile() as release_gpg_file:\n        await download_file(session, release_url, release_file.name)\n        await download_file(session, release_gpg_url, release_gpg_file.name)\n\n        print(\"Verifying signature of Release with Release.gpg.\")","sourceCodeStart":87,"sourceCodeEnd":123,"githubUrl":"https://github.com/dotnet/aspnetcore/blob/294cab2f9b2e03af6b953820c7ab497c3c8b7ad9/eng/common/cross/install-debs.py#L87-L123","documentation":"When --force-check-gpg is set, fetch_and_decompress verifies each Packages.gz against the SHA256 recorded in the Release file before decompressing it. A mismatch raises a plain Exception. This catches a tampered or stale package index whose checksum does not match the signed Release — a stronger integrity guarantee than the per-deb check because it is anchored to a GPG-signed file.","triggerScenarios":"fetch_and_decompress fetches the Packages.gz for a suite/component, then if check_sig is True it fetches the Release file via gpgv-verified download, parses it for the sha256 of the relative path (e.g., main/binary-amd64/Packages.gz), and compares. A mismatch raises immediately.","commonSituations":"Mirror mid-sync (Release updated before Packages.gz or vice versa); partial mirror that ships Release but not the matching Packages; CDN serving a cached Release with fresh Packages; man-in-the-middle tampering.","solutions":["Use a mirror known to be fully synced (deb.debian.org, snapshot.debian.org pinned to a timestamp, or a local complete mirror).","Wait for the mirror sync window to pass and re-run — Release/Packages skew is transient.","If you trust the transport, you may drop --force-check-gpg to skip this check, but prefer fixing the mirror.","Pin to a snapshot mirror (e.g., snapshot.debian.org/archive/debian/<timestamp>/) so Release and Packages are atomically consistent."],"exampleFix":"# before — rolling mirror with Release/Packages skew\npython3 install-debs.py --force-check-gpg --keyring key.gpg \\\n  --suite sid --mirror http://deb.debian.org/debian ...\n# SHA256 mismatch for main/binary-amd64/Packages.gz\n\n# after — pinned snapshot\npython3 install-debs.py --force-check-gpg --keyring key.gpg \\\n  --suite sid --mirror http://snapshot.debian.org/archive/debian/20240101T000000Z/ ...","handlingStrategy":"fallback","validationCode":"# Pre-validate Release/Packages consistency before extracting\nasync with session.get(release_url) as r:\n    release_text = await r.text()\npackages_sha = parse_release_file(release_text, path)\n# compare against the live Packages.gz hash; abort early if mismatch\nif packages_sha is None:\n    print(f'No checksum recorded for {path} in Release; mirror layout mismatch')","typeGuard":null,"tryCatchPattern":"try:\n    content = await fetch_and_decompress(session, mirror, arch, suite, component, check_sig, keyring)\nexcept Exception as e:\n    if 'SHA256 mismatch for' in str(e) and 'Packages' in str(e):\n        # Release/Packages skew — fall back to a snapshot mirror\n        snapshot_mirror = f'http://snapshot.debian.org/archive/debian/{pin}/'\n        content = await fetch_and_decompress(session, snapshot_mirror, arch, suite, component, check_sig, keyring)\n    raise","preventionTips":["Pin to a snapshot mirror so Release and Packages.gz are atomically consistent.","Avoid rolling mirrors during their sync window.","Run checksum verification in CI to catch mirror skew early.","Keep --force-check-gpg on for production rootfs builds."],"tags":["python","debian","checksum","gpg","integrity","install-debs","rootfs"],"analyzedSha":"294cab2f9b2e03af6b953820c7ab497c3c8b7ad9","analyzedAt":"2026-08-06T20:08:02.189Z","schemaVersion":2},"datasetVersion":"2026-08-06T23:17:07.152Z"}