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.
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
| Path | Audit action | Where it appears | Analyzer rule |
|---|---|---|---|
| Clone / fetch over Git | git.clone, git.fetch | Git events only (API include=git/all, enterprise export, streaming) | git-clone-burst (high) |
| Source archive download | repo.download_zip | Regular audit log | archive-download-burst (medium) |
| Visibility change | repo.access with visibility: public | Regular audit log | repo-made-public (high) |
| Transfer to another owner | repo.transfer, repo.transfer_outgoing, repo.transfer_start | Regular audit log | repo-transferred (high) |
| Fork to a personal account | policy: private_repository_forking.enable | Regular audit log | private-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_tokenthe 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_zipin 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.accesswithvisibility: 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.destroyin bulk (three or more within an hour triggersrepo-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:
- Export the list (in the analyzer, the finding's entity chips, or Events filtered on
git.cloneand the attacker IP → Events CSV). - Scan the full history of each repository for secrets, including deleted files and old commits. The attacker has the history too.
- Rotate every credential found, starting with cloud keys and database connection strings. Where they are cloud keys, continue with the cloud pivot.
- Map infrastructure knowledge. Terraform, Helm charts and CI templates tell the attacker your network. Review what they reveal.
- 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
- Leaked GitHub token: what to do and how to investigate
- How to export the GitHub audit log: the Git events section
- The fictional northwind-labs walkthrough: 38 repositories cloned from a VPS
- Glossary: Git events