A closer look at repo-jacking
Repo-jacking is a supply chain attack that exploits GitHub username changes. When a GitHub user renames their account, repos associated with that username move to the new namespace. An attacker can then register the old username and create repositories under that name, potentially tricking developers into pulling malware from what looks like a familiar URL.
The impact of a successful repo-jacking attack is limited by GitHub's tombstoning mechanism. When a repository meets certain usage thresholds—such as a specific number of clones—its old owner-and-repository name combination is permanently retired after a rename. Even if an attacker claims the freed username, they cannot recreate a repository with that exact name pair. Only projects below those thresholds are exposed, meaning a successful attack is unlikely to hit a widely used codebase.
Attack vectors and mitigating factors
A key aggravating factor is that GitHub automatically redirects renamed repositories. That keeps existing clones and fetches working, but it also means users rarely notice a dependency has moved. The redirect disappears when an attacker creates a repository under the old name, so workflows that were pulling from the original URL silently start fetching the attacker's code instead.
Package managers provide a partial defense. Replacing a package on npm, PyPI, or crates.io requires the maintainer's credentials—repo-jacking alone does not grant those. The one notable exception was Packagist, which automatically crawled GitHub for updates and ingested the repo-jacked hautelook/phpass in May 2022; Packagist has since fixed that behavior.
The realistic exposure is when you fetch code straight from GitHub rather than through a package manager. That usually happens in three scenarios:
- Referencing GitHub Actions in workflows.
- Importing Go modules that point directly at GitHub repositories.
- Using git submodules.
For all three, pinning to a specific commit ID eliminates the risk. Git commit IDs are SHA-1 hashes, and an attacker would need a preimage attack to substitute malicious code—theoretically possible but not a practical concern. Submodules are always pinned by default; you only need to be careful when updating them. Go modules can be additionally protected with a go.sum file that records cryptographic hashes of all dependencies.
Detecting repo-jacking via the repository ID
Every GitHub repository has a numeric repository ID that remains constant across renames. Repo-jacking creates a brand-new repository, which always gets a new ID. Developers—or package managers—can therefore detect an attack by verifying the ID hasn't changed. Using PyGithub, an example check looks like this:
from github import Github
def check_repo_id(owner, name, expected_id):
repo = Github().get_repo(f"{owner}/{name}")
if repo.id != expected_id:
raise Exception(f"⚠️ Repo ID mismatch: expected {expected_id}, got {repo.id}!")
if repo.renamed_at:
raise Exception("⚠️ This repo has been renamed!")
This approach works for auditing your own dependencies manually, and it's simple enough to build into any automated system that interacts with GitHub's “Get a repository” endpoint.
Why Repo-Jacking Still Matters
Repo-jacking is a supply chain attack vector that becomes possible when a GitHub user changes their username. GitHub mitigates this risk with a tombstoning algorithm that permanently retires the names of popular repositories after a rename, reducing the chance that an attacker can register the old username and silently take over the associated project.
However, most software is not fetched directly from GitHub. Package managers act as an intermediary, adding an extra layer of defense against repo-jacking. If your project does pull dependencies straight from GitHub, the most effective safeguard is to pin dependencies to a specific commit ID. That removes any ambiguity about which version of the code you are actually using, regardless of what happens to the repository's username or ownership.
The Shift Toward Build Provenance
One of the most promising developments in supply chain security is the growing adoption of build provenance via OpenID Connect (OIDC). GitHub Actions workflows can now send an OIDC token to a package manager when uploading build artifacts. The token contains metadata—such as the repository name, the specific workflow file that ran, and the repository ID—that the package manager can evaluate before deciding whether to publish a new package version.
Because the token includes the repository ID, it inherently blocks repo-jacking and other forms of supply chain attack that rely on identity changes. This mechanism is already integrated into several major ecosystems, including npm, PyPI, Homebrew, and RubyGems.org.
Cross-organization work on the Supply-chain Levels for Software Artifacts (SLSA) framework is another important step forward in hardening the software supply chain. The official site, https://slsa.dev/, provides a thorough overview of the framework and its goals.
Notes
- The username https://github.com/gh is already taken, so this isn't actually possible. ↩
- Learn more about how we permanently retire in our documentation. ↩



