Skip to content

This tool is not affiliated with, endorsed by or sponsored by GitHub, Inc. or Microsoft Corporation. GitHub and GitHub Actions are trademarks of GitHub, Inc. Other names are trademarks of their respective owners.

Detecting GitHub repo exfiltration and mass cloning

Find source code theft in the GitHub audit log: git.clone bursts, ZIP archive downloads, repos made public or transferred, forks, and what each leaves behind.

Published on 6 min read

TL;DR. Source code leaves GitHub in five ways: git.clone/git.fetch, repo.download_zip, making a repository public (repo.access), transferring it (repo.transfer*), or forking it into a personal account. The first two require Git events or archive events. For clones, that means Enterprise Cloud and a 7-day retention window. The pattern to look for is one actor or token pulling many distinct repositories from one IP within an hour. When you see it, treat every secret in those repositories' history as leaked.

The question "what did they take?" comes right after "how did they get in?". It decides whether you face a leak, an extortion attempt or a supply-chain risk (stolen code reveals your infrastructure and often contains credentials). Large-scale repository theft is a real pattern. In May 2026, GitHub itself reported unauthorized access to its internal repositories after an employee device was compromised through a poisoned third-party VS Code extension.

The five exfiltration paths and their evidence

PathAudit actionWhere it appearsAnalyzer rule
Clone / fetch over Gitgit.clone, git.fetchGit events only (API include=git/all, enterprise export, streaming)git-clone-burst (high)
Source archive downloadrepo.download_zipRegular audit logarchive-download-burst (medium)
Visibility changerepo.access with visibility: publicRegular audit logrepo-made-public (high)
Transfer to another ownerrepo.transfer, repo.transfer_outgoing, repo.transfer_startRegular audit logrepo-transferred (high)
Fork to a personal accountpolicy: private_repository_forking.enableRegular audit logprivate-forking-enabled (low)

ATT&CK maps the first two to T1213.003 Data from Information Repositories: Code Repositories. Making a repository public is T1567 Exfiltration Over Web Service, and a transfer is T1537 Transfer Data to Cloud Account.

Mass cloning: reading git.clone

GitHub documents git.clone as "a repository was cloned" and notes that it is not available in the web interface, only through the REST API, streaming or exports. A clone event gives you the actor, the repository (repository, repository_public), the time, the transport (transport_protocol_name: http or ssh) and, when available, actor_ip, country, user agent and the hashed_token of the credential used.

What normal looks like: a developer clones a handful of repositories they work on, from their usual IP, with their usual git/2.x user agent. CI systems fetch the same repositories over and over.

What theft looks like:

  • Breadth. Many distinct repositories, including ones the actor never pushed to.
  • Speed. Clones spaced seconds apart, which is scripted, not a person.
  • Origin. One IP that the actor never used, often a hosting provider, in a country new for the token.
  • Credential. The same hashed_token the developer uses, but a different user agent.

The analyzer's git-clone-burst rule groups git.clone by actor and IP and fires at 10 distinct repositories within 60 minutes. That threshold is deliberately conservative. A new hire cloning the monorepo and its satellites on day one can trigger it, which is why the finding lists the repositories and the IP, so you can decide quickly.

When there are no Git events

Without Git events the analyzer says so in its coverage notes ("mass-cloning detection is blind"). That is the common case on GitHub Free and Team, where the API and Git events are not available. Your options:

  • Check repo.download_zip in the regular log. Archive downloads are logged there.
  • Use the repository Traffic page. Anyone with push access can see full clones for the past 14 days, as aggregate counts with no actor or IP. See viewing traffic to a repository.
  • Treat everything the stolen credential could read as exposed. It is the only defensible assumption.

Archive downloads

repo.download_zip records "a source code archive of a repository was downloaded as a ZIP file". It is a quieter alternative to git clone: no Git client, a normal browser or curl user agent, and it appears in the regular audit log. archive-download-burst fires at 5 distinct repositories in an hour for one actor. Archives do not contain Git history, which slightly limits what the attacker got. Secrets that were deleted in later commits are not in a ZIP of the current tree.

Repositories made public or transferred

These are loud, but attackers use them for extortion and for "leak it and walk away" operations:

  • repo.access with visibility: public: private code becomes downloadable by anyone within seconds. Even if you revert it quickly, assume it was mirrored.
  • repo.transfer_start / repo.transfer / repo.transfer_outgoing: the repository, issues and history move to another owner.
  • repo.destroy in bulk (three or more within an hour triggers repo-mass-delete) is often paired with a ransom note. A deleted repository can usually be restored within 90 days (fork networks have exceptions), so check before you negotiate with anyone.

Scoping: from "cloned" to "exposed"

Once you have the list of repositories, the job changes from detection to impact:

  1. Export the list (in the analyzer, the finding's entity chips, or Events filtered on git.clone and the attacker IP → Events CSV).
  2. Scan the full history of each repository for secrets, including deleted files and old commits. The attacker has the history too.
  3. Rotate every credential found, starting with cloud keys and database connection strings. Where they are cloud keys, continue with the cloud pivot.
  4. Map infrastructure knowledge. Terraform, Helm charts and CI templates tell the attacker your network. Review what they reveal.
  5. Prepare for disclosure and extortion. Legal and communications decisions depend on what the code contained (customer data in fixtures, licensed code, security-sensitive components).

Prevent and detect sooner

  • Stream the audit log (Enterprise Cloud) so that Git events outlive the seven-day window. See audit log streaming.
  • Enable IP display and an IP allow list. Clones from a VPS then fail instead of succeeding.
  • Keep the private-forking policy off, and alert on private_repository_forking.enable.
  • Keep secrets out of repositories: enable secret scanning and push protection. The analyzer flags secret_scanning_push_protection.bypass.

FAQ

Can I see who cloned my private GitHub repository?

In an organization on GitHub Enterprise Cloud, yes: git.clone events record the actor, repository, time, and (with IP display enabled) the source IP and token hash. They are kept for seven days and are not in the web interface export. On other plans the audit log does not give you per-clone records.

Related articles

What the GitHub audit log does not record: 180-day and 7-day retention, plan limits for API and Git events, hidden IPs, no file contents or secret reads.
GitHub organization takeover indicators in the audit log: new owners, 2FA requirement off, SAML SSO changed, IP allow list off, audit streaming removed.
A GitHub PAT or OAuth token leaked? Revoke it, find its hashed_token in the audit log, check new IPs, countries and clones, then scope what the token reached.

This tool is not affiliated with, endorsed by or sponsored by GitHub, Inc. or Microsoft Corporation. GitHub and GitHub Actions are trademarks of GitHub, Inc. Other names are trademarks of their respective owners.