BIPI
BIPI

GitHub Enterprise Incident Response: Audit Logs, Secret Scanning, and Post-Compromise Steps

Cybersecurity

A working GitHub Enterprise IR playbook covering the audit log API, push protection signals, PAT enumeration, OAuth app review, fork detection, and the post-compromise sequence for source code exposure.

By Arjun Raghavan, Security & Systems Lead, BIPI · June 9, 2024 · 8 min read

#github#source-code#ir

Source code incidents are slow burning. The attacker rarely makes noise. The signals are an OAuth app you do not recognize, a secret scanning alert nobody triaged, or a fork that should not exist. GitHub Enterprise IR is mostly log archaeology against the audit log API.

1. The audit log API

The org-level audit log API is the single source of truth. The web UI shows the last few months. The API can paginate beyond. Authenticate as an org owner with a fine-grained PAT scoped to audit_log:read.

GET /orgs/{org}/audit-log?phrase=actor:alex+action:repo.*&per_page=100

The actions that matter during IR: repo.access, repo.transfer, repo.create_fork, oauth_application.create, personal_access_token.access_granted, team.add_member, repo.add_member, and protected_branch.policy_override.

2. Secret scanning hits

Push protection blocks new commits with secrets but does not remediate the historical ones. Pull all open secret scanning alerts via the API and triage by severity of the secret type.

GET /orgs/{org}/secret-scanning/alerts?state=open&secret_type=aws_access_key_id,gcp_service_account,github_pat

Every active credential is treated as exposed even if the alert was opened minutes ago. Rotate first, investigate second. The investigation tells you the blast radius; rotation buys you the time to do it calmly.

3. PAT and OAuth app enumeration

Personal access tokens are the persistent identity in GitHub. Org owners can list tokens with org access via the API.

GET /orgs/{org}/personal-access-tokens

Filter to tokens with repo or admin:org scopes. For each unfamiliar token, check the access_granted_at timestamp against your incident window. Revoke aggressively. Users complain less than CISOs do when a token re-issue is the price of certainty.

OAuth apps are the other persistence mechanism. List installed OAuth apps under org settings and cross-reference with audit log oauth_application.create entries. Anything created during the incident window by anyone other than your org owners gets revoked.

4. Fork detection and code exfiltration

Repo.create_fork events tell you when someone forked. For private repos, forks are visible only inside the org but the user can clone locally. The harder signal is repo.access events from unusual IP addresses or unusual user agents. The audit log includes hashed_token and programmatic_access_type fields that distinguish CI clone from human clone.

If you suspect bulk cloning, run a query on the git events.

GET /orgs/{org}/audit-log?phrase=action:git.clone+created:>2024-05-30 grouped by actor

5. Post-compromise containment

Once you have a scope, the containment sequence on GitHub is short but unforgiving. Do not delete the user account first; doing so loses audit trail clarity. Suspend instead.

  1. Suspend the user from the org: DELETE /orgs/{org}/members/{username} or use the Enterprise admin API to disable across the enterprise.
  2. Revoke all PATs and SSH keys for the user: the user's own settings page, or via SCIM if linked.
  3. Audit branch protection rules in repos the user could modify; restore from your IaC if changed.
  4. For each repo the user accessed, force-rotate any credentials referenced by that repo's GitHub Actions secrets and environment variables.
  5. Enable secret scanning on remaining repos if it was not on, and turn on push protection org-wide.

6. The hard question of code in attacker hands

If you confirm bulk clone or fork, the next steps are commercial, not technical. Inventory the secrets in the repo history (truffleHog or gitleaks against the cloned mirror), notify customers whose data could be in the codebase, and engage your legal team on disclosure obligations. The IR runbook ends at the moment legal hold begins.

6 months default, 180 days+ with streaming
Audit log API retention (Enterprise)
47 days
Median secret scanning alert age in noisy orgs
90 days
Recommended PAT max lifetime

GitHub IR rewards orgs that already stream audit logs to a SIEM and have a documented PAT lifecycle. Without those two things, every incident becomes a fresh archaeological dig.

Read more field notes, explore our services, or get in touch at info@bipi.in. Privacy Policy · Terms.