On July 2, Jacob Ginesin posted a five‑page paper to arXiv demonstrating that a signed Git commit's hash can be rewritten into a new, valid hash without the signing key — and that, in his tests, GitHub still labels those rewritten commits "Verified." The submission included a public tool that runs all three attacks and two demo repositories that reproduce the effect on GitHub.
Jacob Ginesin's demonstration and its public artifacts
Ginesin, a PhD student at Carnegie Mellon University and a cryptographic auditor at Cure53, published a short paper and accompanying code showing an operational problem in how forges treat signed commits. His tool produces alternate, valid encodings of an existing signed commit; those alternate encodings carry different commit hashes even though the files, author, and dates are unchanged. The two demo repositories in the paper reproduce the behaviour on GitHub: the malleated commits still show the "Verified" badge.
How signature malleability lets one commit become many hashes
The root cause is signature malleability combined with what Git includes in a commit's hash. A commit's hash is computed over everything inside it, including the raw bytes of the signature header. Many signatures have multiple, equally valid encodings; changing those raw signature bytes changes the commit's hash without altering the committed files. Ginesin calls the resulting effect "hash chain malleability."
The paper describes three distinct technical routes that cover every GPG scheme GitHub verifies, plus S/MIME (X.509):
- ECDSA keys: flip the signature by turning the value s into n − s. Both forms are valid; the flipped form passes a local git verify‑commit and also earns GitHub's "Verified" badge.
- RSA and EdDSA keys: add an extra, ignored field to the signature's "unhashed" section — the portion the signature deliberately does not cover. The signature still verifies, but the commit bytes and its hash change. Local checks and GitHub accept this form.
- S/MIME (X.509) keys: rewrite a length field in the signature's DER structure into a longer, non‑standard form. A strict local check using gpgsm rejects that encoding, yet GitHub still marks the commit "Verified" in the paper's reproducer.
Why GitHub's "Verified" records can be misleading in practice
GitHub does not normalize a signature before checking it: it accepts non‑canonical ECDSA values, leaves OpenPGP fields untouched, and accepts non‑standard S/MIME encodings. The forge then files a "Verified" record tied to that specific commit hash and does not re‑check it later. As a result, a commit remains labeled "Verified" even after the signing key is revoked. Pushing an original commit and its malleated twin to two branches produces divergent histories in GitHub's compare view — one commit appears ahead, the other behind — despite identical files.
That behavior has operational consequences. Systems that block or flag bad commits by hash can be evaded by re‑pushing the same content under a different, still‑"Verified" hash the blocklist has never seen. Deduplication, provenance logs, and reproducible‑build records that key on the commit hash inherit the same exposure. A compromised or hostile mirror could hand cloners validly signed commits whose hashes differ from the canonical forge's.
Importantly, this is not a technique for slipping different code past a signature check: every copy contains identical files, so a pinned hash still fetches the expected content or fails to fetch it. The flaw lies in treating the raw commit hash — without canonicalizing the signature — as a one‑of‑a‑kind identifier for the content.
Canonicalization as the known, forge‑side fix
The paper points to a straightforward mitigation: canonicalize signature encodings before trusting the commit hash. The core move is not novel — the Bitcoin ecosystem faced the same ECDSA symmetry and fixed it by accepting only the "low‑S" form, and later by moving signatures out of the transaction ID with SegWit. Ginesin's suggested remedies rhyme with that lesson: normalize or canonicalize signatures prior to computing or trusting a hash.
There is no CVE and no vendor advisory attached to the paper, and Ginesin says there is nothing to change inside an individual developer's repository; the work is for the forges and for tooling that trusts raw commit hashes. The obvious place to begin, the paper says, is the S/MIME case, where GitHub accepts encodings that a strict local check rejects.
What this means for developers, forges, and supply‑chain tooling
- Developers and maintainers: the existing advice to pin to a full commit hash still holds. Pinning stopped earlier tag‑hijack attacks cited in the paper (2025's tj‑actions/changed‑files and 2026's trivy‑action), and a pinned hash will continue to fetch the expected content.
- Forges and code hosting platforms: the corrective action is on the forge side — normalize signatures before filing a "Verified" record or before trusting a raw commit hash. Without that step, a forge's badge can be truthful about who signed a particular encoding while still being misleading about the hash as a permanent identifier.
- Provenance, deduplication, and blocking tools: these systems should verify and canonicalize signatures prior to trusting commit hashes. Some systems that also pin an independent hash of fetched files (the paper cites fixed‑output derivations used by Nix as an example) retain a backstop; those that stop at a verified commit hash do not.
Ginesin says he reported the issue to GNU and Git in January and to GitHub in March; as of the paper's publication on July 2, neither Git nor any forge had addressed the problem. The technical fix is well understood; the remaining question the paper leaves on the table is whether platform operators will adopt signature canonicalization before relying on commit hashes.




