How to Investigate Unauthorized GitHub Activity

Estimated read time 13 min read

A branch has been force-pushed. The activity appears under your GitHub username, but you did not make the change. Your browser sessions look familiar, and two-factor authentication is enabled. What should you investigate next?

Start with the credential that authorized the operation. GitHub access can come from an OAuth token, a personal access token, an SSH key, an installed application, or automation. An account name is an important clue, but it does not establish which person or device used that access.

This guide explains how developers and repository owners can establish what happened, contain the relevant access, and recover without overstating the evidence. The examples use Windows and PowerShell for local checks. Access to organization Git audit data depends on your GitHub plan and permissions. If that data is unavailable, you can investigate using account and repository checks.

Stop ongoing harm while preserving the essentials

If unauthorized activity is continuing, revoke the affected access immediately from a trusted device. Preserve readily available evidence, but do not delay containment to complete the investigation. If teammates are available, evidence collection and containment can proceed in parallel.

Record when the suspicious activity occurred, including the time zone, and convert timestamps from different systems to UTC when comparing them. Capture the affected repository and branch or tag, the recorded actor, and the old and new commit identifiers. Preserve relevant activity links and original log exports in restricted storage, noting when each item was collected and when response actions were taken. If you need to annotate the evidence, work from copies rather than modifying the originals.

For a force push, preserve available copies of the previous history before attempting a repair. Avoid running unfamiliar code, build tasks, or workflows from the affected repository. If a suspicious workflow is running, stopping it may be more urgent than reconstructing the entire timeline.

Then check whether an expected release job, IDE, scheduled script, or teammate action explains the change. Unexpected activity is a reason to investigate; it is not, on its own, proof of credential theft.

Separate the account from the person behind it

The investigation has three distinct questions: what changed, which access path permitted it, and who controlled that access. Evidence that answers one question may leave the others open.

Repository Activity links pushes, merges, force pushes, and branch changes to commits and authenticated users. Use it to establish the repository event and affected history. A commit’s author name or email is a different kind of information and should not be substituted for evidence of who authenticated the push. See Repository activity.

EvidenceUseful forDoes not establish by itself
Repository ActivityChange type, affected branch, associated commits and accountThe physical person or device responsible
Personal Security logAccount events around the incident windowA complete record of repository reads and writes
Organization Git audit eventsRecorded Git operations and available authentication metadataEvery way repository content can change
Local history and configurationCommands and credential paths present on a workstationThat no other device or process used the credential

The personal Security log covers the previous 90 days. Review it for relevant authorization and account changes, then export the useful records. A normal-looking browser session list does not rule out unauthorized programmatic access. See Personal security log.

Treat user-agent strings and country codes as supporting clues. A Windows-flavored Git user agent is consistent with that client family; it does not identify an individual machine. Network location may reflect a VPN, proxy, or cloud service. The strongest conclusions come from independent records that point to the same activity.

Map the access that could have caused the change

Review the account’s sessions, personal access tokens, authorized OAuth apps, GitHub App user authorizations, and SSH authentication keys. Repository and organization owners should separately review app installations, deploy keys, collaborators, teams, and automation.

Keep authorization types distinct. Removing a user’s app authorization and removing an organization’s app installation address different access paths. Likewise, an SSH authentication key grants a different capability from a key used only to sign commits.

For each plausible credential, record its identifier when available, permission scope, accessible repositories, and expected owner or tool. Distinguish observed impact from possible exposure: a credential may have permission to reach repositories for which you have no evidence of activity.

The scope of the response depends on what this inventory reveals. An unexplained credential with write access to one repository requires a different investigation from an integration with broad organization access. The repository where the incident was first noticed may not be the only one affected.

Identify the workstation credential path

GitHub CLI and Git may use different credentials. First inspect the CLI’s authentication state without displaying its token. See GitHub CLI authentication status.

gh auth status –hostname github.com

Do not add –show-token. Review diagnostic output privately before sharing it; usernames, paths, and repository names can also be sensitive.

GitHub CLI gives GH_TOKEN and GITHUB_TOKEN precedence over stored credentials. In PowerShell, check whether either variable exists without printing its value. See GitHub CLI environment variables.

‘GH_TOKEN’,’GITHUB_TOKEN’ | ForEach-Object {
    [pscustomobject]@{
        Name = $_
        Present = Test-Path “Env:$_”
    }
}

Next inspect the configured Git credential helpers. Run the command in the relevant trusted repository context when possible, because configuration can vary by location. See Git configuration reference.

git config –show-origin –get-all credential.helper

This lists helper configuration; it does not retrieve a stored password. However, a custom helper can contain sensitive inline configuration, so do not paste the output into a public issue without reviewing it.

If Git Credential Manager is installed, inspect its GitHub accounts and available commands. See Git Credential Manager account management.

git credential-manager github list
git credential-manager github –help

These checks help identify which credentials the workstation could have supplied, but they do not show which one was actually used during the incident. Git may also authenticate through an SSH remote, IDE, container, CI runner, or another machine, so consider those paths as part of the investigation.

Local shell history and repository configuration can provide additional context. Search the known development folders first and inspect remote configuration without executing code from the affected project. Keep linked worktrees in mind as well: because they may use a .git file rather than a .git directory, a search limited to .git/config can miss them.

Treat shell history as supporting evidence rather than a complete record. If you find no matching command, that establishes only that it was not present in the history you examined; GUI clients, other shells, deleted history, and commands executed remotely may leave no entry there.

No matching command in shell history means only that you did not find one in that history. GUI tools, deleted records, other shells, and remote use remain possible.

Export Git audit evidence before it expires

For eligible GitHub Enterprise Cloud organizations, an authorized owner can retrieve Git events through the audit-log REST API. Because these events are retained for only seven days, collect them early in the investigation. They are not available through GraphQL, while GitHub Enterprise Server behavior depends on the version and configuration in use. See Organization audit logs and Git event retention.

Git events also have an important coverage limitation: operations performed through the browser or REST or GraphQL APIs are not included. For example, a pull-request merge performed in the browser may change a branch without producing a corresponding Git push event. Repository Activity and other audit records should therefore be reviewed alongside the Git-event data.

Where authentication metadata is available, preserve fields such as programmatic_access_type, token_id, hashed_token, relevant timestamps, and repository details. GitHub currently documents some token metadata for Git events as a public-preview capability, so the exact fields available may change. See Correlating audit events with access tokens.

To correlate operations, filter the exported Git events for the same hashed_token. This can connect multiple recorded operations to the same credential without exposing the underlying token. Use the exported Git-event data for this comparison rather than assuming that a normal audit-log search for the hash will return the same records.

A worked example: what the evidence supports

Imagine an exported clone event and a later push event for the same repository carry the same token hash. Repository Activity also records a force push in the relevant window. This is a hypothetical example, not a reported incident.

The matching hash connects the recorded Git operations to one credential. The repository view establishes the history-changing action. Together, they provide enough evidence to trace that credential’s activity and revoke its access.

They do not establish how the credential was obtained, which computer held it, or who operated that computer. Those questions require additional evidence. Record the distinction in the incident notes rather than filling the gap with a location guess.

If you lack organization Git logs, continue with repository activity, account records, integration logs, and available endpoint evidence. Ask the repository or organization owner to preserve what they can. “Credential not identified from available evidence” is a valid finding.

Use the Events API only for supplementary context

The Events API can help locate recent activity, but it is not a complete forensic record or a real-time monitoring feed. GitHub documents possible delays of 30 seconds to six hours. Visibility also depends on the authenticated user and permissions. See REST API endpoints for events.

For recent account context, this PowerShell example uses GitHub CLI pagination until all available events have been retrieved. It outputs timestamps and repository names for returned push events:

$me = gh api user –jq ‘.login’
if ($LASTEXITCODE -ne 0) { throw ‘Cannot identify account.’ }
gh api –paginate “users/$me/events?per_page=100” `
    –jq ‘.[] | select(.type == “PushEvent”) | [.created_at, .repo.name] | @tsv’

Pagination retrieves all records currently available through the endpoint, but the available history is still limited. Preserve any useful results along with the collection time and the account used to retrieve them. If no relevant events appear, continue the investigation using the other available evidence.

Revoke server access and then clear local credentials

Once the evidence points to questionable access, revoke that access at GitHub. Revoked tokens can no longer authenticate Git or API requests. Revoking an OAuth or GitHub App user authorization also revokes the tokens associated with that authorization. See Token expiration and revocation.

If the suspected authorization is GitHub CLI, review it under GitHub’s application settings. The CLI manual states that revoking its authorization invalidates tokens generated by GitHub CLI across your devices. Consider which legitimate workflows may be interrupted by the revocation. See GitHub CLI logout and server revocation.

Review other plausible paths at the same time: personal access tokens, SSH and deploy keys, installed apps, automation secrets, and unexpected collaborators. Secure account sign-in and recovery settings if account takeover is plausible. A password change alone is not evidence that every programmatic path has been removed.

Local logout is a cleanup step. gh auth logout removes the selected account’s local authentication configuration; it does not revoke the token at GitHub. After server-side revocation, remove the stale entry, replacing YOUR_USERNAME with the account being cleaned up.

gh auth logout –hostname github.com –user YOUR_USERNAME

Use the credential manager’s supported logout or removal process to clear any cached credentials, and check its account list afterward. Avoid deleting unrelated credentials.

Verify exactly what was revoked

Verify the same credential or authorization path that you intended to revoke. For example, gh auth status reports the authentication configuration available to GitHub CLI, but it does not test every token or SSH key on the machine. An environment variable may also cause the CLI to use a different credential. See GitHub CLI authentication status and GitHub CLI environment variables.

Record the server-side revocation and its time. If the old credential remains available through its original client and can be tested safely, confirm that GitHub rejects it without displaying or copying the secret. Do not keep a compromised token solely for the purpose of performing this test.

Interpret the result carefully. A network failure, permission denial, and authentication failure indicate different things, while a successful request may simply have used another credential. If the old authentication path cannot be isolated, document that limitation rather than treating the test as complete verification.

Reauthenticate only the tools that are still required, and do so from a trusted environment. If you use GitHub CLI’s browser-based login flow, authenticate first and then confirm the API identity associated with the new session. See GitHub CLI login.

gh auth login –hostname github.com –git-protocol https –web
gh api user –jq ‘.login’

That identity check confirms which GitHub account the CLI is using for API requests. Git itself may still obtain credentials through a different path, so review its authentication configuration separately.

Recover the repository and assess downstream impact

Credential containment and repository recovery should be handled separately. After preserving evidence, identify a known-good state with the repository owner and coordinate the repair with collaborators. A hurried force push can destroy useful context or overwrite legitimate work made during the incident.

Inspect changes to application code, workflow files, release scripts, tags, and deployment configuration. Determine whether suspicious changes reached a build, package, release, or production deployment. Restoring the branch does not repair artifacts already published from it.

If an untrusted workflow ran with access to secrets or a privileged runner, treat those resources as potentially compromised. Preserve run records, contain suspicious automation, and rotate downstream credentials where exposure is plausible. GitHub’s Actions security guidance explains why workflow permissions, secrets, and runner trust boundaries matter. See GitHub Actions secure use reference.

Review branch protection or rulesets, including force-push controls, required review, status checks, and bypass permissions. Verify that the protection rule actually covered the affected branch and the relevant actors. See Protected branches and bypass permissions.

If compromise of the workstation remains plausible, investigate the endpoint using trusted security tooling and your incident-response process. A clean scan does not rule out the possibility that a token was copied. Avoid issuing fresh credentials on a machine whose integrity is still in doubt.

Close with a finding the evidence can support

A useful incident summary should distinguish confirmed findings from unresolved questions. Record what activity was established, which repositories were affected, which credential was identified, or where uncertainty remains, and what containment and recovery actions were taken. Any unresolved issues should have a clear owner for follow-up.

After recovery, continue monitoring repository changes and authorization events involving the accounts, tools, and integrations reviewed during the investigation. Teams that may need to investigate incidents beyond GitHub’s native retention periods should also establish ongoing audit collection and an appropriate retention policy.

The goal is not to reconstruct more than the evidence can support. Establish what GitHub accepted, remove unauthorized access, repair the resulting changes, and clearly document any questions the available evidence cannot answer.

Related Readings from WebDigestPro

Subscribe to our newsletter!

Dimitrios S. Sfyris https://aspectsoft.gr

Dimitrios S. Sfyris is Founder of AspectSoft and a seasoned professional with 17 years of experience across software development, academic research, and enterprise practice. Holding an M.Sc. in Systems Engineering and a Ph.D. in Fuzzy Logic and Expert Systems, he bridges rigorous academic insight with real-world innovation, specializing in full-stack web applications, SaaS platforms, and scalable API architectures.

You May Also Like

More From Author

+ There are no comments

Add yours