GitHub Actions Supply Chain Incident: Forensic Response Checklist for Compromised CI/CD Pipelines

Retool's new app builder is where AI-generated code ships safely
Building apps with AI is easy. Getting them to production safely is another story.
Supply chain attacks against GitHub Actions have two targets that differ from traditional intrusions: your pipeline secrets (tokens, cloud credentials, signing keys stored as GitHub Secrets or environment variables) and your build artifacts (binaries, packages, container images that downstream systems and customers trust).
The Megalodon attack (May 2026) injected a malicious codeql_analysis.yml workflow into 5,561 repositories using a compromised GitHub Actions token. The TanStack attack poisoned 160 npm packages using a malicious preinstall hook. Both attacks exfiltrated credentials that appeared in the pipeline environment.
This checklist walks through response in the order that minimizes ongoing damage: containment first, then forensics, then remediation, then hardening.
Phase 1: Containment (Do These First)
Containment stops the bleeding before you investigate. Do not skip to forensics while the attacker still has active access.
1. Disable compromised workflows immediately. If you know which workflow file was tampered with, rename it so GitHub stops scheduling it:
git mv .github/workflows/compromised.yml .github/workflows/compromised.yml.disabled
git commit -m "Disable compromised workflow pending investigation"
git push
Do not delete it yet: the file is forensic evidence.
2. Revoke all GitHub Actions tokens and secrets in scope. Navigate to Settings > Secrets and variables > Actions for each affected repository and organization. Rotate every secret that could have been read by the compromised workflow. Prioritize cloud credentials (AWS, GCP, Azure), npm tokens, Docker registry credentials, and signing keys.
3. Suspend the compromised runner (if self-hosted). If the attack used a self-hosted runner, take it offline immediately. The runner machine may be fully compromised, not just the workflow. Do not wipe it before forensics.
4. Freeze downstream deployments. Stop any automated deployment pipelines that consume artifacts built by the compromised workflow. Even if the artifact appears clean, treat it as untrusted until you verify the build chain.
5. Preserve state before any cleanup. Before rotating secrets, export the current GitHub Actions audit log for the affected organizations:
gh api /orgs/{org}/audit-log --paginate > audit_log_$(date +%Y%m%d).json
This log is your primary forensic source and is only retained for 90 days.
Phase 2: Forensics (Establish Scope)
After containment, determine exactly what the attacker accessed, modified, and exfiltrated.
6. Review the GitHub Actions audit log for the attack window. Filter for workflow runs, secret accesses, and permission changes:
gh api "/orgs/{org}/audit-log?phrase=action:secrets.actions_secret&per_page=100" \
--paginate | jq '.[] | {action, actor, created_at, repo}'
Key events to search for: secrets.actions_secret (secret accessed), workflows.created, workflows.updated, repo.create, org.add_member.
7. Audit all workflow files modified during the attack window.
git log --all --since="YYYY-MM-DD" --until="YYYY-MM-DD" -- .github/workflows/
git diff BEFORE_HASH AFTER_HASH -- .github/workflows/
For the Megalodon pattern, look specifically for newly created codeql_analysis.yml files. For TanStack-style attacks, check package.json scripts for unexpected additions to preinstall, postinstall, or prepare.
8. Identify every secret the compromised workflow had access to.
Check the workflow file's env: blocks, with: parameters in steps, and any secrets referenced as ${{ secrets.* }}. Every secret referenced is potentially exfiltrated.
9. Check for artifact tampering. For npm packages: compare the published package content hash against the hash of a locally built version from the same commit:
npm pack --dry-run # shows what would be published
sha256sum package-name-version.tgz # compare against registry artifact
For container images: compare the image digest of the pushed image against a local rebuild from the same Dockerfile and commit.
10. Audit downstream use of compromised artifacts. Search your organization for any system that installed the compromised package version or pulled the compromised container image during the attack window. These systems must be treated as potentially compromised.
11. Check for persistence artifacts in the repository.
git log --all --oneline --diff-filter=A -- .github/workflows/
The --diff-filter=A flag shows only commits that added new files. Review every workflow file added during and after the attack window. Also check for new GitHub Actions within the workflow using external actions at non-pinned refs (e.g., uses: attacker/action@main instead of a pinned SHA).
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Phase 3: Remediation (Close the Access)
Remediation is not complete until every credential the attacker may have touched is rotated and every artifact they may have modified is verified or replaced.
12. Rotate all exposed secrets in every affected system. Secret rotation order: most privileged first.
- Cloud IAM credentials (AWS access keys, GCP service account keys, Azure service principals)
- Code signing certificates and GPG keys
- npm tokens and package registry credentials
- Docker registry credentials
- Database passwords accessible from pipeline environments
- API keys for third-party services called from workflows
For each secret: generate a new credential in the originating system first, update the GitHub Secret, verify the pipeline works with the new credential, then revoke the old one.
13. Revoke and re-issue GITHUB_TOKEN permissions if org tokens were involved. If the attacker used an OAuth token or GitHub App credential (not just GITHUB_TOKEN), revoke it through Settings > Developer settings > OAuth Apps or GitHub Apps.
14. Remove malicious workflow files and any attacker-created content. After forensic copies are preserved:
git rm .github/workflows/compromised.yml
git log --all -- .github/workflows/attacker-file.yml # confirm removal from all branches
git push --all
Check all branches, not just the default branch. Attackers commonly persist in non-default branches.
15. Yank compromised package versions from registries. For npm:
npm deprecate package-name@compromised-version "Compromised in supply chain attack. Do not use."
For packages where silent injection occurred, coordinate with the registry on unpublishing if deprecation is insufficient.
16. Notify downstream consumers. Any organization or developer that installed a compromised package version or pulled a compromised image should be notified. Include the affected version range, the attack window, and the recommended action (upgrade to clean version, rotate credentials on any system where the package ran).
Phase 4: Hardening (Prevent Recurrence)
These controls reduce the blast radius of any future supply chain attack against your pipelines.
17. Pin all external GitHub Actions to commit SHAs.
Change every uses: actions/checkout@v4 in your workflows to a pinned SHA:
# Before (vulnerable to tag mutation):
- uses: actions/checkout@v4
# After (pinned to exact commit):
- uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683
The SHA is immutable. A tag can be moved by an attacker who compromises the action's repository.
18. Restrict GITHUB_TOKEN permissions to minimum required.
permissions:
contents: read # only what the job actually needs
Default GITHUB_TOKEN permissions in many organizations are too broad. The Megalodon attack relied on workflows that had contents: write and packages: write when only read was needed.
19. Require code review for workflow file changes.
In Branch Protection settings, add .github/workflows/ to the required review paths. This requires a second developer to approve any workflow modification before it merges.
20. Enable secret scanning and push protection. Navigate to Settings > Security > Code security. Enable Secret Scanning and Push Protection. Push Protection blocks commits that contain detectable credential patterns before they reach the repository.
21. Audit third-party GitHub App permissions. Navigate to Settings > Installed GitHub Apps for your organization. Review every app's requested permissions and the repositories it can access. Remove apps that are no longer in use. Any third-party app with write access to your workflows is a potential supply chain attack vector.
22. Configure Dependabot for GitHub Actions. Add a Dependabot configuration for GitHub Actions to track version updates:
# .github/dependabot.yml
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "weekly"
This creates PRs when new versions of actions you use are released, making it easier to review and update pinned SHAs.
23. Set up automated workflow run monitoring. Create an alert for unexpected workflow runs using the GitHub audit log API or by connecting audit log streaming to your SIEM. Alert on: new workflow files created, workflow permission escalations, and secret access from unexpected workflow names.
24. Document and test your response runbook. Run a tabletop exercise using this checklist within 30 days of any supply chain incident in your environment or in a major dependency. The response timeline matters: the TanStack attack ran for 11 days before discovery, during which every affected developer system should be treated as potentially compromised.
The bottom line
GitHub Actions supply chain incidents have a different forensic surface than traditional intrusions. The evidence is in workflow files, audit logs, and artifact hashes rather than system logs and network captures. Containment requires disabling workflows and rotating secrets before investigation, because the attacker has active automated access to your environment every time the pipeline runs. After this incident, pin all external actions to SHAs and restrict GITHUB_TOKEN permissions. Both controls would have limited the blast radius of both the Megalodon and TanStack attacks in 2026.
Frequently asked questions
How do I tell if a GitHub Actions workflow was tampered with?
Compare the current workflow file content against the expected content using git history. Run `git log --all -p -- .github/workflows/filename.yml` to see every change ever made to the file. Look for commits from unfamiliar authors, commits made through the GitHub web UI (which bypasses branch protection in some configurations), and additions of external action references with non-pinned tags. Also check for steps that exfiltrate environment variables (common patterns: `env | curl`, `printenv | base64`, or steps that POST to external URLs).
Should I wipe and rebuild all systems that ran a compromised workflow?
It depends on what the compromised workflow did. If the workflow only exfiltrated secrets from environment variables, the systems that ran it are not necessarily compromised. Rotate the exposed secrets and verify no unauthorized access occurred using those credentials. If the workflow executed arbitrary code on a self-hosted runner, treat that runner machine as fully compromised and rebuild it. If the workflow pushed a compromised artifact that was subsequently deployed to a system, that system needs to be treated as compromised.
What is the difference between a compromised GitHub Action and a compromised workflow?
A compromised GitHub Action is a reusable action in a third-party repository (e.g., `actions/checkout`, a community action) that an attacker has modified to perform malicious operations when called. A compromised workflow is a `.yml` file in your repository's `.github/workflows/` directory that an attacker has modified directly. Both result in malicious code running in your pipeline, but they require different response steps. A compromised action affects every repository using it; a compromised workflow affects only that repository.
How long does GitHub retain Actions audit logs?
GitHub retains Actions audit logs for 90 days for GitHub Free and 90 days for GitHub Enterprise Cloud. After 90 days, audit log events are not available through the API or UI. Organizations conducting post-incident investigations more than 90 days after an event occurred will not have access to Actions audit data. For this reason, export audit logs immediately during incident response and configure audit log streaming to an external SIEM if you have compliance or forensics requirements beyond 90 days.
How do I detect a compromised GitHub Actions runner that is exfiltrating secrets?
Compromised GitHub Actions runner detection: monitor for unusual network connections from runner hosts to external IPs not associated with GitHub's IP ranges or your legitimate deployment targets — outbound connections during build steps to unknown IPs are a strong exfiltration signal. Check runner process trees: a runner that spawns reverse shell processes, downloads additional tooling (curl, wget to non-package-manager domains), or accesses credential files outside the expected workflow path is suspicious. Enable GitHub Actions debug logging (ACTIONS_STEP_DEBUG=true) for investigation. For self-hosted runners: use your EDR to monitor the runner process (actions-runner on Linux, actions.runner.*.exe on Windows) for unexpected child processes and network connections. GitHub-hosted runners are ephemerally destroyed after each job, eliminating persistence — prefer GitHub-hosted runners over self-hosted for build security.
What SLSA framework controls would have limited the blast radius of the Megalodon and TanStack supply chain attacks?
SLSA (Supply-chain Levels for Software Artifacts) provides a graduated framework for hardening build pipelines against exactly the attack patterns observed in both incidents. SLSA Level 2 requires that build provenance is generated and signed by the build service itself -- under this requirement, the Megalodon attack's injected codeql_analysis.yml would have produced a provenance document for each tampered workflow run that could be compared against the expected provenance from the legitimate workflow. Deviation would have been detectable within the deployment verification step before any artifact reached production. SLSA Level 3 adds a requirement that the build environment is isolated and ephemeral -- self-hosted runners that persist between jobs, which were a key factor in several 2026 supply chain incidents, do not satisfy Level 3. For the TanStack attack, SLSA artifact provenance would have allowed downstream consumers to verify that the compromised npm package version was not built from the expected source commit and tag, flagging the poisoned package before installation. Implementing SLSA Level 2 for your most critical internal packages and requiring SLSA provenance verification before deploying any third-party package in a production workflow provides meaningful protection against both workflow injection and dependency confusion attacks.
Sources & references
Free resources
Critical CVE Reference Card 2025–2026
25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.
Ransomware Incident Response Playbook
Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.
Get threat intel before your inbox does.
50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.
Unsubscribe anytime. We never sell your data.

Founder & Cybersecurity Evangelist, Decryption Digest
Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
