On May 18, 2026, an automated campaign pushed 5,718 malicious commits to 5,561 GitHub repositories in six hours. The attacker used throwaway accounts with forged identities like build-bot, auto-ci, and pipeline-bot to modify .github/workflows/ files. The goal was not to inject code into application logic but to backdoor the CI infrastructure itself.
This is the Megalodon attack. It exposes how GitHub Actions workflows become a supply-chain vector when automation infrastructure gains write access to workflow files. The permission model, trigger types, and secret access rules create a trust boundary that breaks when an attacker controls the workflow definition.
The Attack Surface
GitHub Actions workflows are YAML files that define automation steps. They run on GitHub-hosted or self-hosted runners with access to:
- Repository secrets (API keys, cloud credentials, deployment tokens)
- The
GITHUB_TOKENwith scoped permissions to the repository - Network egress to arbitrary endpoints
- Arbitrary code execution in the runner environment
The attack surface is the workflow file itself. If an attacker can modify .github/workflows/*.yml, they control what runs in the CI environment.
What Megalodon Changed
The malicious commits injected steps into existing workflows or created new workflow files that:
- Exfiltrated repository secrets to attacker-controlled endpoints
- Injected backdoor dependencies into build artifacts
- Modified deployment scripts to push compromised containers or binaries
- Planted persistence by adding workflow triggers on future pull requests
The attacker targeted repositories with:
- Public visibility (easier to fork and submit pull requests)
- Existing CI workflows (less suspicious to add steps)
- Active maintainers (higher chance of merge without scrutiny)
Permission Boundaries in GitHub Actions
GitHub Actions has three main permission boundaries that matter for this attack:
| Boundary | Scope | Risk |
|---|---|---|
GITHUB_TOKEN | Scoped to the repository, configurable per workflow | Can push code, create releases, modify issues if permissions are broad |
| Secrets context | Repository secrets, environment secrets, organization secrets | Full plaintext access if workflow runs with secrets context |
| Trigger type | pull_request, pull_request_target, push, workflow_dispatch | Determines whether forks can access secrets |
The pull_request_target Trap
The pull_request_target trigger is the most dangerous for this attack. It runs in the context of the base branch (not the fork) and has access to repository secrets. This is intended for workflows that need to comment on pull requests or deploy preview environments.
If a workflow uses pull_request_target and checks out the pull request code without validation, an attacker can inject malicious steps that run with secret access:
name: Preview Deploy
on:
pull_request_target:
types: [opened, synchronize]
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
ref: ${{ github.event.pull_request.head.sha }}
- name: Deploy preview
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
# Attacker controls this script via the PR
./deploy-preview.sh
The pull_request trigger is safer. It runs in the fork’s context and does not have access to base repository secrets. But many repositories use pull_request_target for legitimate reasons (commenting, labeling, deploying previews), which creates the opening.
Detection Signals
When a workflow file is modified to exfiltrate secrets or inject malicious steps, several signals appear:
- Diff anomalies: New
curlorwgetcommands to unfamiliar domains, base64 encoding of secret values, or obfuscated shell commands - Trigger changes: Adding
pull_request_targetorworkflow_dispatchto workflows that previously usedpull_request - Secret access expansion: New
secrets.*references in steps that did not previously access secrets - Network egress: Outbound connections to pastebin-like services, file-sharing sites, or attacker-controlled domains
- Author identity: Commits from bot-like usernames with no prior contribution history
Megalodon used forged author identities to blend in. The commits appeared to come from build-bot@users.noreply.github.com, which looks like an automated service account. This bypasses human review if maintainers assume bots are trusted.
Workflow Modification as Supply-Chain Attack
The supply-chain risk is not just in dependencies. It is in the automation that builds, tests, and deploys those dependencies. If an attacker controls the workflow, they control:
- What gets built (injecting malicious dependencies)
- Where it gets deployed (pushing to attacker-controlled registries)
- What secrets are used (exfiltrating credentials for later use)
This is harder to detect than a malicious package because workflow files are not scanned by traditional dependency tools. They are infrastructure-as-code, but they run with production credentials.
The Agent Problem
As coding agents (Copilot Workspace, Cursor, Aider) gain the ability to generate and modify CI configs, the attack surface grows. An agent that writes a workflow file may:
- Add a
pull_request_targettrigger without understanding the security implications - Include a step that logs secrets for debugging
- Pull in a third-party action from an untrusted source
The agent does not have malicious intent, but it creates the same backdoor. The trust model assumes humans review workflow changes, but agents generate code faster than humans can audit.
Sandboxing Agent-Generated Workflows
To reduce risk when agents write workflow files:
- Restrict trigger types: Block
pull_request_targetandworkflow_dispatchin agent-generated workflows unless explicitly approved - Audit secret access: Flag any new
secrets.*references for manual review before merge - Limit runner permissions: Use
permissions: {}to deny allGITHUB_TOKENscopes by default, then grant only what is needed - Network egress control: Run workflows in environments with egress filtering to block unknown domains
- Workflow signing: Require GPG-signed commits for workflow file changes, with a separate approval process
Example of a locked-down workflow:
name: Agent-Generated Build
on:
pull_request: # Not pull_request_target
permissions: {} # No GITHUB_TOKEN permissions
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build
run: |
npm ci
npm run build
# No secret access, no deployment, no network calls
This workflow can build and test but cannot exfiltrate secrets or deploy artifacts.
Failure Modes
The most likely failure modes when defending against workflow backdoors:
- False negatives: Malicious steps that use legitimate-looking actions (e.g.,
actions/github-scriptwith inline JavaScript that exfiltrates secrets) - Review fatigue: High volume of workflow changes from agents leads to rubber-stamp approvals
- Inherited risk: Third-party actions that are compromised after initial review (supply-chain attack on the action itself)
- Self-hosted runners: If runners are persistent and not ephemeral, malware can persist across workflow runs
The Megalodon attack succeeded because it targeted repositories with weak review processes and high trust in bot-like commit authors.
Technical Verdict
Use GitHub Actions workflows with strict boundaries when:
- You enforce
pull_requesttriggers for untrusted contributions - You audit all
secrets.*references and restrict access to specific jobs - You use ephemeral runners and deny
GITHUB_TOKENpermissions by default - You treat workflow file changes as infrastructure changes requiring separate approval
Avoid or heavily sandbox when:
- Agents generate workflow files without human review of trigger types and secret access
- You rely on
pull_request_targetfor preview deployments without validating the PR code first - You use self-hosted runners with persistent state or network access to internal systems
- You assume bot-like commit authors are trustworthy without verifying the change content
The Megalodon attack shows that CI workflows are not just automation. They are privileged code that runs with production credentials. Treat them like you would treat deployment scripts or infrastructure-as-code: with strict review, limited permissions, and the assumption that any modification is a potential backdoor.