On 13 October 2026, Meta began notifying about 1,500 people, mostly US employees, that an outside group had used "an employee's stolen credentials" to access some of its GitHub repositories. Meta told SecurityWeek that about 99% of the data taken was business data, mainly source code, and the rest was personal data placed in repositories by mistake. It detected the access a few weeks before notifications went out.
You don't need Meta's scale to have Meta's problem. The useful question is this: if one engineer's credentials leaked today, could you list every way they reach your code and cut each one off within the hour? This guide walks through that audit with gh and the REST API.
What's affected
Any GitHub organization where a member's credentials reach private repositories. A password reset doesn't end most of that access. GitHub's credential authorization API lists the types that matter on SSO organizations: personal access token, SSH key, OAuth app token and GitHub app token. On top of those sit deploy keys, which belong to a repository rather than a person, and GitHub App installations.
Two documented details catch teams out. First, GitHub says that unless you restrict them, "any personal access token (classic) can access organization resources until the token expires." Second, when an owner revokes a fine-grained token, "SSH keys created by the token will continue to work."
How to check if you're exposed
Set two variables for the person you're investigating:
ORG=your-org
LOGIN=suspect-login
1. Pull Git events first. They expire. GitHub's audit log reference says that on GitHub Enterprise Cloud, Git events are available through the REST API "only with 7-day retention." The git.clone event doesn't appear in the web interface at all.
gh api --method GET "orgs/$ORG/audit-log" --paginate \
-f phrase="actor:$LOGIN action:git.clone" -f include=git -f per_page=100 \
--jq '.[] | [.created_at, .repo, .transport_protocol_name] | @tsv'
Run it again with action:repo.download_zip, which records source archive downloads. If you hold a leaked token value, hash it with echo -n TOKEN | openssl dgst -sha256 -binary | base64 and search with hashed_token:"VALUE", URI-escaping the hash. GitHub notes that token searches don't return Git events, so export those separately.
2. List the user's SSO credential authorizations.
gh api --method GET "orgs/$ORG/credential-authorizations" -f login="$LOGIN" --paginate \
--jq '.[] | [.credential_type, .credential_id, .token_last_eight, .credential_accessed_at, .authorized_credential_expires_at] | @tsv'
A token with no expiry and a recent credential_accessed_at you can't explain goes to the top of the list.
3. Review fine-grained personal access tokens. In the UI: organization Settings, Personal access tokens, Active tokens, then filter by Owner. The REST endpoint GET /orgs/ORG/personal-access-tokens only accepts GitHub App tokens. With an app installation token in APP_TOKEN:
curl -s -H "Authorization: Bearer $APP_TOKEN" \
"https://api.github.com/orgs/$ORG/personal-access-tokens?last_used_after=2026-09-01T00:00:00Z" |
jq -r '.[] | [.owner.login, .token_name, .repository_selection, .token_expires_at, .token_last_used_at] | @tsv'
4. Sweep deploy keys. The REST response includes who added each key and when it was last used, which gh repo deploy-key list doesn't show.
gh repo list "$ORG" --limit 1000 --json nameWithOwner --jq '.[].nameWithOwner' |
while read -r REPO; do
gh api "repos/$REPO/keys" \
--jq '.[] | [.id, .title, .read_only, .added_by, .last_used] | @tsv' |
awk -v r="$REPO" '{print r "\t" $0}'
done
5. List GitHub App installations and outside collaborators.
gh api "orgs/$ORG/installations" --paginate \
--jq '.installations[] | [.app_slug, .repository_selection, .created_at] | @tsv'
gh api "orgs/$ORG/outside_collaborators?filter=2fa_insecure" --paginate --jq '.[].login'
gh api "orgs/$ORG/members?filter=2fa_disabled" --paginate --jq '.[].login'
An app with repository_selection set to all reaches every repository, including ones created after you approved it.
What to do now
-
Cut the user's SSO authorizations in one call. This revokes their PATs, SSH keys, OAuth app tokens and app user tokens for the org. It runs as a background job, and they must re-authorize anything legitimate.
gh api -X POST "orgs/$ORG/credential-authorizations/$LOGIN/revoke"To remove a single credential instead, use
DELETE /orgs/ORG/credential-authorizations/CREDENTIAL_ID. -
Revoke fine-grained tokens from the Active tokens page, or with
POST /orgs/ORG/personal-access-tokensand a body of"action": "revoke"pluspat_ids, from a GitHub App. -
Delete deploy keys you can't account for with
gh api -X DELETE repos/OWNER/REPO/keys/KEY_ID. Preferread_onlykeys for anything that stays. -
Restrict classic tokens. Under Settings, Personal access tokens, Settings, open the Tokens (classic) tab and set "Restrict personal access tokens (classic) from accessing your organizations". On the same page, set a maximum lifetime and keep administrator approval on for fine-grained tokens. GitHub notes the lifetime policy blocks non-compliant tokens but doesn't revoke them.
-
Confirm OAuth app access restrictions are on. GitHub warns that without them, "any OAuth app authorized by an organization member can also access the organization's private resources."
-
Keep Git events longer than seven days. Export or stream the audit log to your own storage so a breach found weeks later still has clone records.
-
Rotate secrets found in the affected repositories, as Meta did, and scan history for personal data that shouldn't be there.
Why it matters
Meta's spokesperson put it plainly: "This was not a vulnerability in any Meta product." The entry point was a valid login, and valid logins come with tokens, keys and app grants that outlive the password. An audit that stops at "reset the password and enforce 2FA" leaves most of those in place.
The retention window is the quiet risk. Meta found the access weeks after it began. With a seven-day window on Git events, an organization that doesn't export them can lose the record of what was cloned before anyone starts looking.
Try it yourself
Run step 2 against your own login. Count the credentials you have authorized for the organization, note how many have no expiry, and check when each was last used. Then decide which ones you'd revoke if your laptop disappeared tonight.
Click through it at your own pace, or open it full screen.
Quick check
Does revoking a fine-grained token also cut off SSH keys it created?
No. GitHub's documentation says SSH keys created by a revoked fine-grained token continue to work, so review SSH key authorizations separately.
Can you see git.clone events in the audit log web interface?
No. GitHub lists git.clone as available only through the REST API, audit log streaming, or JSON and CSV exports, with 7-day REST retention on Enterprise Cloud.
Which endpoint revokes all of one user's SSO credential authorizations at once?
POST /orgs/ORG/credential-authorizations/USERNAME/revoke, which queues the revocation as a background job.
Sources
- SecurityWeek: Meta says hackers took code, personal data of 1,500 in GitHub breach
- GitHub Docs: REST API endpoints for organizations (audit log, credential authorizations, installations)
- GitHub Docs: REST API endpoints for fine-grained personal access tokens
- GitHub Docs: Audit log events for your organization
- GitHub Docs: Identifying audit log events performed by an access token
- GitHub Docs: Reviewing and revoking personal access tokens in your organization
- GitHub Docs: Setting a personal access token policy for your organization
- GitHub Docs: About OAuth app access restrictions
- GitHub Docs: REST API endpoints for deploy keys
- GitHub Docs: REST API endpoints for outside collaborators
- GitHub CLI manual: gh api