# Deleting a Malicious Workflow Does Not Revoke the Key

> GhostAction used compromised GitHub accounts to place credential-stealing workflows across thousands of repositories. The visible file is only one part of the incident: teams also need to revoke the entry credential, rotate what the workflow could read, and inspect what that authority could reach.

- **Author:** Kubilay Tunca
- **Published:** 2026-10-15
- **Category:** For Developers
- **Tags:** GitHub Actions, Supply Chain Security, Incident Response, Secrets Management
- **Canonical URL:** https://cyber-security-in-plain-english.com/post/developers/news/deleted-workflow-does-not-revoke-key

---

A new file called `security-audit.yml` appears under `.github/workflows`. Its name sounds responsible. Its job does the opposite: it reads credentials available to the build, searches the repository and its history for more, and sends what it finds away. Delete the file, close the alert, move on.

That last step is the trap.

On 13 October 2026, GitGuardian updated its investigation of the GhostAction campaign with a much larger burst. Its dataset showed more than 30,000 repositories targeted across 3,000 users and organisations between 8 and 12 October, following smaller waves in 2025 and earlier in 2026. The researchers attributed the new spread to about 1,900 compromised GitHub accounts and counted attempts to extract 3,174 secrets from public repositories' continuous-integration jobs ([GitGuardian: GhostAction Returns](https://blog.gitguardian.com/ghostaction-github-actions-supply-chain-attack-returns/)).

Those figures describe attempted collection, not 3,174 confirmed stolen credentials. GitGuardian said the large batches on 8 and 9 October ran, while GitHub prevented later batches on 10 and 12 October from executing. The distinction matters. So does the sequence: the attacker already had a credential that could write to each repository before the malicious automation arrived.

A poisoned workflow is the visible part of this incident. The useful response must also find and revoke the key that opened the repository, replace every secret the workflow may have reached, and review the systems behind those secrets. Otherwise the file disappears while the authority that created it remains.

## The campaign began before the workflow ran

GhostAction is a campaign against developer infrastructure. The operator uses access belonging to a real maintainer, adds or changes a GitHub Actions workflow, and lets GitHub's own automation run the new instructions. The workflow then tries to collect credentials from the build environment, the checked-out files, or the repository's history.

The newest burst was not the campaign's first appearance. GitGuardian originally documented GhostAction in September 2025. Between 31 August and 30 September 2026, it then observed the malicious workflow in 772 public repositories belonging to 373 users and organisations. Most of those runs were held for approval, and the researchers confirmed successful exfiltration of 26 secrets from 13 repositories in the data they collected. By 5 October, they could see effective cleanup in only 124 of the 772 repositories ([GitGuardian: campaign timeline and cleanup findings](https://blog.gitguardian.com/ghostaction-github-actions-supply-chain-attack-returns/)).

The October burst changed both scale and behaviour. GitGuardian's 13 October update says the first new payload searched the working tree and full Git history using patterns for cloud credentials, GitHub and GitLab tokens, and AI-provider keys. Later versions added nearby text around Amazon Web Services key identifiers, apparently to find the matching secret value. The operator first deployed versions to small groups, then pushed one version to roughly 28,000 new repositories within about 20 minutes.

Independent research captured part of the same movement. Socket reported that two compromised maintainer accounts placed a workflow across 346 repositories on 8 October, including projects to which those maintainers had legitimate write access. Socket found that the workflow searched both current files and Git history, and that the affected runs executed rather than waiting for approval ([Socket: New GhostAction Wave](https://socket.dev/blog/ghostaction-cloud-credentials)). The Hacker News later reported GitGuardian's broader count and the three evolving payload versions on 15 October ([The Hacker News: GhostAction compromises about 1,900 GitHub accounts](https://thehackernews.com/2026/10/ghostaction-compromises-about-1900.html)).

The repository did not grant itself access. A valid account or token arrived first. Public reporting points to stolen personal access tokens, compromised account sessions, infostealer records, and reused credentials as likely sources. GitGuardian also found some victims from earlier waves among the October victims, which supports a less dramatic but more useful explanation: access that was never revoked can be used again.

That changes the incident boundary. If a malicious commit appears under a maintainer's identity, deleting the commit removes one artefact. It does not invalidate the maintainer token used to push it. If the workflow read a package-registry token, deleting the workflow does not stop that registry token. If it found an old cloud key in Git history, deleting the current file does not erase the key from the cloud provider.

The first question is therefore not “which file should we remove?” It is “which authority allowed this file to arrive?”

## Why GitHub Actions turns a write into wider authority

A source repository contains text. A continuous-integration system turns some of that text into actions. It checks out code, installs tools, runs tests, builds packages, signs artefacts, publishes releases, and sometimes deploys production. To do that work, it receives credentials and a temporary GitHub token. A workflow file is therefore closer to executable infrastructure than ordinary documentation.

GitHub Actions secrets are stored values made available to a workflow only when the workflow refers to them. Repository, environment, and organisation secrets can have different scopes and policies. GitHub's documentation notes that repository and organisation secrets are read when a workflow run is queued, while environment secrets are read when the job that references the environment starts ([GitHub Docs: Secrets reference](https://docs.github.com/en/actions/reference/security/secrets)). Those timing and scope rules are useful controls, but they cannot rescue a secret deliberately exposed to hostile instructions.

Imagine a release workflow with four credentials. One token can publish a package. A second pushes a container image. A third reaches a cloud account used for staging. A fourth is an API key used during a test. A malicious workflow need not attack each service directly. It asks the automation system to hand over the credentials, then uses them elsewhere.

GhostAction made that request look like routine maintenance. Names such as “Security Audit” and “GitHub Actions Security” fitted the directory in which they appeared. In earlier waves, the malicious workflow named particular secrets already referenced by legitimate workflows. In October, newer versions also searched files and history. That is a meaningful expansion because a secret can outlive the line that once used it.

Git history is designed to remember. Removing `AWS_SECRET_ACCESS_KEY` from today's configuration does not remove it from yesterday's commit. Rewriting history can remove the text from normal views, but it still cannot make an already copied credential safe. Revocation does that. Rotation replaces the old value with a new one and updates the legitimate consumers that still need it.

The temporary `GITHUB_TOKEN` deserves a separate note. GitHub creates it for a job, limits it to the repository, and expires it after the job. Its permissions still matter because a write-capable token may alter repository contents, releases, issues, or packages during the run. GitHub recommends setting the default permission to read-only and granting write access only to the jobs that require it ([GitHub Docs: Secure use reference](https://docs.github.com/en/actions/reference/security/secure-use)). A short lifetime narrows the window. Narrow permissions reduce what can happen inside that window.

Long-lived deployment credentials create the larger tail. An npm token may publish a poisoned package after the GitHub run ends. A cloud key may create resources, read stored data, or mint more credentials according to its policy. An SSH key may open a server long after the repository has been cleaned. The workflow is one point of collection; each credential leads to a different downstream incident.

This is why a secret count is not a damage count. One exposed hostname may be harmless. One package-registry token with release authority may affect every downstream user who installs the next version. Response priority follows authority, use, and evidence, not the number of strings found.

## The file, the account, and the downstream secrets are three incidents

A useful way to scope GhostAction is to split it into three connected layers. Each layer has its own evidence and its own completion test.

The first layer is repository integrity. Which workflow files changed? Which branches, tags, releases, packages, or pull requests changed with them? Did the malicious workflow run? Was an existing workflow modified rather than a new one added? A clean default branch is necessary, but the review must include every repository and ref the compromised identity could write.

The second layer is GitHub identity. Which user, personal access token, OAuth authorisation, GitHub App credential, SSH key, or browser session performed the write? What else could that identity access? GitGuardian found malicious commits carrying victims' normal identities. A familiar author name proves that GitHub accepted the credential; it does not prove that the human typed the commit.

The third layer is downstream authority. Which secrets were available to each executed job, present in the checked-out tree, or recoverable from history? Where do those credentials work? What did those systems record after the likely exposure time? A deployment key leads to a server review. A PyPI or npm token leads to package-release history. A cloud credential leads to provider logs, policy review, and a search for persistence created with that key.

Treating the layers separately prevents two common mistakes. The first is stopping when the repository looks clean. The second is rotating every secret in the company without learning which jobs ran or which credentials were in reach. The first response is too small. The second creates avoidable outages and can destroy useful evidence.

The order also matters. Preserve the repository, workflow-run, account, and provider records before retention limits or cleanup remove them. Then contain active authority. A responder may need to revoke a credential immediately if it is still being used, but that decision should be recorded with the time and reason. Incident work becomes much easier when each action leaves a receipt.

GitHub's incident-response guidance makes the same broad separation. For a suspected Actions or runner incident, it recommends disabling affected workflows or runners as needed, revoking compromised credentials, rotating repository, environment, and organisation secrets, and updating credentials in external systems that may have been reached ([GitHub Docs: Responding to a security incident](https://docs.github.com/en/code-security/tutorials/secure-your-organization/respond-to-a-security-incident)). The repository repair sits beside identity and downstream repair; it does not replace them.

A team should be able to close each layer with evidence. Repository integrity ends with a reviewed change set and known-good workflow files. Identity containment ends with the offending credential revoked, active sessions reviewed, and the account recovered. Downstream containment ends with exposed secrets replaced and the systems behind them checked for use during the relevant window.

Three layers. Three receipts.

## A practical response for an affected repository

Start with the signal you actually have. Perhaps GitHub, GitGuardian, Socket, or a maintainer reports an unexpected workflow. Perhaps a cloud provider flags a key. Perhaps the only clue is a commit called “Security Audit” that nobody remembers approving. Record the repository, commit hash, actor, workflow name, run identifiers, and discovery time before changing anything.

The next steps form a sequence because later decisions depend on earlier evidence.

1. **Preserve the relevant records before cleanup.** Save the suspicious workflow source, commit metadata, branch and tag references, Actions run pages, logs, artefact metadata, approvals, and the account's security or organisation audit events. Note the earliest suspicious write and every subsequent run. Do not download or execute unknown artefacts on a normal workstation merely to inspect them.

2. **Stop new execution.** Disable the affected workflow or Actions for the repository if necessary, cancel active runs, and prevent unreviewed changes while the team scopes the incident. For an organisation-wide maintainer compromise, temporary restrictions at organisation level may be safer than racing through repositories one by one.

3. **Revoke the entry credential.** Remove the personal access token, SSH key, app authorisation, or session that enabled the malicious write. Reset account authentication where the evidence requires it and review recovery settings and multifactor methods. A password change alone may leave a personal access token working, so verify the exact credential type rather than applying a generic account ritual.

4. **Inventory what actually ran.** A committed workflow and an executed workflow are different exposure states. Map each run to its source revision, event, job, environment, and available secrets. GitGuardian said GitHub prevented the 10 and 12 October batches from running, while the large 8 and 9 October batches executed. Your own run history decides your own response.

5. **Rotate secrets according to reach.** Replace every credential that an executed hostile job could read, plus credentials found in files or history that the payload inspected. Begin with package publishing, cloud administration, production deployment, signing, and identity-management authority. Rotation means invalidating the old value first or as part of a controlled handover, not merely creating a second valid key.

6. **Inspect downstream use from the exposure time.** Check package releases, container pushes, cloud audit events, server logins, deployment records, newly created credentials, and policy changes. Match times and source information where possible. A lack of an obvious outage is not evidence that a publishing token went unused.

7. **Restore repository integrity from a known point.** Remove the malicious workflow from every affected branch and repository. Review nearby commits made by the same identity, release tags, generated packages, and any workflow edits that changed permissions or destinations. Rebuild and republish only after the credentials and source state are trusted.

8. **Write the closure receipt.** Record the entry credential, repositories reached, runs executed, secrets exposed or ruled out, downstream checks performed, rotations completed, and remaining uncertainty. Name an owner for any long-running follow-up. “Workflow deleted” should appear as one line, not the title of the report.

This sequence scales down. A solo maintainer can save the run page, revoke the token, rotate a registry key, inspect release history, repair the workflow, and write a short incident note. A platform team may automate much of the inventory. The evidence questions remain the same.

There is also a limit worth naming. Public researchers can observe public repositories and public commit history. They cannot see every private repository, secret value, provider log, or internal response. GitGuardian's counts help establish the campaign's shape. They cannot tell an individual organisation that its private systems are clean. Only the organisation's own records can do that.

## Prevention should reduce authority, not add another warning

GhostAction used a valid route through compromised identity and trusted automation. A banner telling developers to “watch for suspicious YAML” has limited value when the malicious file carries a plausible name and arrives under a familiar account. Stronger prevention changes what one stolen credential can do.

Begin with write access. Maintainers accumulate repositories, old forks, organisation memberships, personal access tokens, SSH keys, and third-party app grants over years. Remove access that no longer serves current work. Prefer fine-grained credentials with repository limits and expiry over broad, durable tokens. Require phishing-resistant account authentication where the platform and team can support it.

Then narrow the automation token. Set the default `GITHUB_TOKEN` permission to read-only. Grant a write permission in the one job that needs it instead of at workflow level. Separate build from release so an ordinary test job cannot publish a package. Use environments for production credentials and require review for sensitive deployments. A workflow that never receives a release token cannot leak it.

GitHub has added controls aimed at hostile workflow execution. In July 2026, it announced that Actions could hold workflows judged potentially malicious until a collaborator with write access approved them ([GitHub Changelog: potentially malicious workflows held for approval](https://github.blog/changelog/2026-07-28-github-actions-holds-potentially-malicious-workflows-for-approval/)). In September, GitHub made workflow execution protections generally available, allowing policy based on who may trigger workflows and which events may start them ([GitHub Changelog: workflow execution protections](https://github.blog/changelog/2026-09-17-workflow-execution-protections-in-github-actions-generally-available/)). GitGuardian's August and September data shows those holds prevented most observed runs. Its October update also shows that some later batches ran before GitHub stopped subsequent execution.

The lesson is not that approval controls failed. They reduced harm in one wave and did not cover every path in another. Prevention needs layers: restricted writers, narrow workflow permissions, protected environments, short-lived credentials, execution policy, detection, and rapid revocation. No single prompt should carry the whole model.

Package publication deserves its own boundary. Use trusted publishing or workload identity where a registry supports it, so the job receives a short-lived token tied to a specific repository and workflow rather than storing a reusable publishing key. Require an environment or release approval for the publishing step. Check that an ordinary push cannot reach that environment.

Cloud access should follow the same pattern. Prefer short-lived federation from a defined workflow to a narrowly scoped cloud role. Restrict the role by repository, branch, environment, and job claims when the provider supports those conditions. A cloud role capable of deploying one service should not be able to create new administrators across the account.

Finally, detect changes where authority lives. Alert on new or modified files under `.github/workflows`, especially direct pushes to the default branch. Record the actor, credential type, source, and review path. Watch for new personal access tokens and app grants. Keep workflow and audit records long enough to investigate a report that arrives days later.

More alerts are not the goal. Smaller authority and better evidence are.

## The oldest key can be the newest incident

GitGuardian found repositories touched in earlier GhostAction waves among the October victims. It also found workflows from previous periods that had remained in repositories and were later updated to point at a new collection endpoint. That continuity explains why cleanup quality matters more than the date printed on the alert.

A credential does not become harmless because its original leak is old. If it still authenticates, it still opens something. Attackers trade and reuse developer credentials, and different operators may use the same compromised identity for different purposes. GitGuardian found several victim repositories associated with separate cryptomining activity, but the researchers did not conclude that one operator controlled every campaign. Shared access was the simpler explanation.

This makes old tokens a current risk, especially for maintainers whose work spans many projects. A token created for a release script in 2023 may still reach repositories the developer joined later. An organisation secret copied into dozens of repositories may outlive the team that created it. A registry token may remain valid because nobody wants to risk breaking a release.

Inventory solves the first half. For each durable credential, name an owner, purpose, scope, creation date, last use, and expiry. If the owner or purpose is unknown, the credential has already failed a basic operational test. Revoke it or replace it through a controlled change.

A rotation schedule alone does not solve the second half. Teams sometimes rotate a secret by creating a new value, updating the main pipeline, and leaving the old value active “for rollback.” Months later, both work. A real rotation has a revocation event and a test showing that the old value fails.

The same standard applies after an incident. Ask someone to demonstrate the failure, using a safe validation method appropriate to the provider. The old package token cannot publish. The old cloud key cannot authenticate. The old GitHub token cannot list private repositories or push a branch. A ticket saying “rotated” is an intention; a failed old credential is evidence.

There is a human advantage to this approach. Developers do not have to remember every campaign name or malicious filename. They need one repeatable model: any unexpected automation change triggers a review of entry authority, execution, reachable secrets, and downstream use. That model survives the next filename.

## What a good closure statement sounds like

“Removed `security-audit.yml`” describes a code change. It does not describe incident closure.

A defensible closure statement is more specific: the team identified the personal access token used for the write and revoked it at a recorded time; reviewed all repositories reachable by that token; confirmed which hostile workflow runs executed; rotated the named package, cloud, and deployment credentials those runs could read; checked the corresponding provider logs from the first suspicious write; reviewed releases and artefacts; restored the workflows from a known revision; and documented the limits of available evidence.

That sentence is longer because the incident was larger than one file.

GhostAction is useful precisely because its mechanism is ordinary. A stolen developer credential writes trusted automation. Trusted automation reads other credentials. Those credentials lead to systems beyond GitHub. Each step uses a feature meant to help a team ship software.

The fix follows the same chain in reverse. Stop the automation. Revoke the repository authority. Replace the downstream authority. Inspect what each credential could do. Then rebuild from known code and leave evidence for the next person.

Deleting the malicious workflow matters. Make it the beginning of the receipt, not the end.

For practical security guidance without a daily flood, join the newsletter. It is one email per month.

## Sources

- [GitGuardian: GhostAction Returns: 772 Repos Hit, 30k More in Oct](https://blog.gitguardian.com/ghostaction-github-actions-supply-chain-attack-returns/), accessed 2026-10-15
- [Socket: New GhostAction Wave Hits Hundreds of Repos, Expanding to Cloud Credentials and Git History](https://socket.dev/blog/ghostaction-cloud-credentials), accessed 2026-10-15
- [The Hacker News: GhostAction Compromises About 1,900 GitHub Accounts, Targets Over 30,000 Repositories](https://thehackernews.com/2026/10/ghostaction-compromises-about-1900.html), accessed 2026-10-15
- [GitHub Docs: Secrets reference](https://docs.github.com/en/actions/reference/security/secrets), accessed 2026-10-15
- [GitHub Docs: Secure use reference](https://docs.github.com/en/actions/reference/security/secure-use), accessed 2026-10-15
- [GitHub Docs: Responding to a security incident](https://docs.github.com/en/code-security/tutorials/secure-your-organization/respond-to-a-security-incident), accessed 2026-10-15
- [GitHub Changelog: GitHub Actions holds potentially malicious workflows for approval](https://github.blog/changelog/2026-07-28-github-actions-holds-potentially-malicious-workflows-for-approval/), accessed 2026-10-15
- [GitHub Changelog: Workflow execution protections in GitHub Actions generally available](https://github.blog/changelog/2026-09-17-workflow-execution-protections-in-github-actions-generally-available/), accessed 2026-10-15

---

## About the author

Kubilay Tunca — Senior Full Stack Developer and Author. Founded Cyber Security in Plain English to translate complex security concepts into clear, practical advice, and writes the accompanying books on security, privacy, secure development, and AI systems.

## Books by this author

- **The Digital Fortress** — Your Everyday Guide to a Safer Digital Life. A warm, plain-English guide for people with real lives and finite patience. Learn the handful of habits that genuinely protect your money, accounts, and family, and get honest permission to ignore the rest. [Amazon](https://buy.cyber-security-in-plain-english.com/digital-fortress) · [Details](https://cyber-security-in-plain-english.com/books/the-digital-fortress)
- **The Anonymity Playbook** — Digital Survival for Whistleblowers, Journalists, Activists, and Everyone Else. A practitioner’s field manual for journalists protecting sources, whistleblowers, and activists. It explains how the surveillance actually works, what each technique costs you, and exactly where it fails. [Amazon](https://buy.cyber-security-in-plain-english.com/anonymity-playbook) · [Details](https://cyber-security-in-plain-english.com/books/the-anonymity-playbook)
- **Secure Software Development** — Practical patterns for building secure software. A hands-on security guide for developers and IT professionals who ship real software. Build, deploy, and maintain secure systems without slowing down or drowning in theory. [Amazon](https://buy.cyber-security-in-plain-english.com/secure-software-development) · [Details](https://cyber-security-in-plain-english.com/books/secure-software-development)
- **The Secure Harness** — Shipping Production Code with AI Coding Agents. A calm, practical guide to letting agents do useful work inside boundaries you set, enforce, and audit. Ships with 15 copy-pasteable artifacts: hook scripts, permission configs, release gates, and MCP templates. [Amazon](https://buy.cyber-security-in-plain-english.com/secure-harness) · [Details](https://cyber-security-in-plain-english.com/books/the-secure-harness)
- **The AI Native Engineer** — Build, Evaluate, and Ship AI Systems That Work in Production. Sixteen hands-on chapters, one real product. Grow it from a single model call into a retrieved, tool-using, observable, production-grade system, with evaluation treated as a habit from the first feature. [Amazon](https://buy.cyber-security-in-plain-english.com/ai-native-engineer) · [Details](https://cyber-security-in-plain-english.com/books/the-ai-native-engineer)

Full catalogue with contents and intended audience: https://cyber-security-in-plain-english.com/books

_As an Amazon Associate I earn from qualifying purchases. Buying through these links costs you nothing extra and helps pay for the blog._
