Safeguard
Threat Research

Repojacking Explained: Hijacking Abandoned Repository Names

Repojacking lets an attacker claim a renamed or deleted GitHub namespace and serve malicious code to everyone still referencing the old path. Here is how it works.

Marcus Chen
Security Researcher
6 min read

Repojacking is a supply-chain attack in which an adversary registers a GitHub username or organization name that was previously used by a legitimate project but has since been renamed or deleted, then recreates a repository at that old path to serve malicious code to everyone still referencing it. It works because a repository's identity on GitHub is a user/repo path, and countless build scripts, install commands, documentation pages, Go module imports, and dependency manifests point at those paths as if they were permanent. When the original owner renames their account or deletes it, the old path becomes claimable, and whoever grabs it inherits every link, redirect, and automated fetch that still points there. The result is quietly devastating: install commands that ran safely for years suddenly pull attacker-controlled code, with no version bump and no compromised account required.

How repojacking works

The attack hinges on the lifecycle of a namespace. When a user renames their account from oldname to newname, GitHub sets up a redirect so old links still resolve — but that redirect is only protective while nobody else claims oldname. Once the name is free and an attacker registers it, the redirect breaks in the attacker's favor: they now control oldname/project, and requests to that path hit their repository.

Consider a common install pattern baked into documentation and CI:

# a script that fetches from a hardcoded namespace path
curl -sL https://raw.githubusercontent.com/oldname/setup/main/install.sh | bash

If oldname was abandoned and an attacker claims it, that one-liner now downloads and executes their script with the runner's privileges. The same logic hits package ecosystems that resolve directly to repository paths — Go modules, for instance, import by URL (import "github.com/oldname/lib"), so a repojacked namespace can poison a Go build outright. GitHub added a protection that retires the namespaces of popular repositories (those with significant clone activity before a rename) so they cannot be re-registered, but that shields only the most-cloned projects, leaving a very large long tail exploitable.

Real-world repojacking research and incidents

The scale was quantified by Aqua Security's Nautilus research team in June 2023. Analyzing a sample of 1.25 million GitHub repositories, they found that about 2.95% — 36,983 repositories in the sample — were vulnerable to repojacking. Extrapolated across GitHub's more than 300 million repositories, that implied roughly nine million potentially vulnerable projects. Crucially, the researchers did not stop at statistics: they found exploitable cases in repositories linked to major organizations, including Google and Lyft, demonstrating that even sophisticated engineering teams reference namespaces they no longer control.

The mechanism recurs constantly because organizations change names, get acquired, or delete accounts, while their old paths live on in tutorials, Stack Overflow answers, Dockerfiles, and CI configuration that nobody revisits. A rebrand that renames a GitHub organization can, without anyone realizing it, leave a trail of now-claimable references across the internet. Attackers monitor for exactly these freed-up popular names, and because the payload is served from a legitimate-looking GitHub path under a familiar-looking name, the deception is hard to spot at a glance.

How to detect and defend against repojacking

Defense combines namespace hygiene with pinning references to something immutable:

  • Never delete or rename critical namespaces casually. If you must rename an organization or user, keep the old account and re-create placeholder repositories at the old paths so the names cannot be claimed by anyone else.
  • Pin to immutable commit hashes, not branch names. Referencing a specific commit SHA, or vendoring the code, means a hijacked namespace serving new content cannot silently alter what you fetch. A moving main reference is the vulnerable pattern.
  • Audit for external namespace references. Grep your build scripts, Dockerfiles, CI configs, documentation, and Go imports for hardcoded github.com/user/repo paths, then verify each referenced account still belongs to who you think it does.
  • Prefer package registries with integrity guarantees. Where possible, consume code through a package manager with lockfiles and checksums rather than raw repository fetches.
  • Monitor for rename and deletion events in dependencies you rely on, since a renamed upstream is the trigger that opens the window.

How Safeguard helps

Repojacking is a visibility problem before it is anything else: the risk lives in forgotten references to namespaces you no longer control, scattered across code and configuration. Safeguard's continuously ingested SBOM and dependency mapping give you an exact inventory of what your projects actually pull in and from where, so a dependency resolving to an external repository path is a known, tracked fact rather than a buried surprise. Software composition analysis resolves dependencies — including Go modules and other sources that map directly to repository paths — and flags those that depend on mutable references an attacker could hijack. Griffin AI evaluates whether a fetched source suddenly changes behavior, catching the moment a hijacked namespace starts serving different code, and container security scanning inspects images for build steps that fetch from external namespaces during assembly. When a risky reference is found, automated fix pull requests pin it to an immutable commit or a vetted package version and drive the change through your pipeline. For a broader view of how Safeguard's supply-chain coverage compares, see our comparison page.

A repository name feels permanent until the person who owned it walks away. Pin what you fetch to immutable hashes, guard the namespaces you rename, and audit the paths your builds still trust.

Frequently Asked Questions

What makes a GitHub repository vulnerable to repojacking? A repository becomes vulnerable when its owning username or organization is renamed or deleted, freeing the old namespace for anyone to re-register, while references to the old user/repo path still exist in scripts, imports, or documentation. GitHub retires the namespaces of very popular repositories, but the long tail of less-cloned projects remains claimable.

How is repojacking different from typosquatting? Typosquatting relies on a victim mistyping a name and installing a lookalike package. Repojacking requires no mistake at all: the victim's existing, correct references to a real path keep working, but that path now resolves to an attacker who claimed a namespace its original owner abandoned. The link was right when it was written and only became malicious later.

Why are Go modules especially exposed to repojacking? Go modules are imported directly by repository URL, such as github.com/user/lib, so the module identity is the namespace path itself. If that namespace is freed and repojacked, a Go build can resolve and compile attacker-controlled code without any separate package registry in between to provide checksums or maintainer verification.

How can pinning to a commit hash prevent repojacking? Branch and tag references point at whatever content currently lives at a path, so a hijacked namespace can serve new malicious code under the same reference. Pinning to a specific commit SHA ties your fetch to exact, immutable content, so even if the namespace changes hands, the hash will not match the attacker's replacement and the fetch fails safe.

Start auditing your dependency sources at app.safeguard.sh/register, and find integration guides at docs.safeguard.sh.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.