If your CI/CD pipeline holds AWS keys, npm tokens or AI service API keys, one hijacked GitHub account is enough to send all of them out of your organisation within minutes. That is what happened in an attack disclosed this week, and it matters both to companies that build software in-house and to those who outsource it.

What happened to the repositories

According to The Hacker News (published 12 November 2026), citing analysis by Aikido Security, the attack began on Tuesday. The attacker used stolen credentials for 4,837 GitHub accounts to plant a secret stealer in repositories those accounts could write to. It ran in at least 1,806 of them.

Each attempt started with a test pull request titled docs: dependency audit notes, opened from a branch named wfx-docs, to check whether the account could write to the repository. These test pull requests appeared in 61,764 repositories. SafeDep counted 8,093 repositories that received malicious commits, and said the real total is higher because it only searched default branches of public repositories.

The worrying part: after pushing the code, the attacker usually reverted the change within minutes and deleted the run logs. A glance at your commit history may show nothing unusual.

What the stealer takes from a runner

It collects everything a workflow can reach: GitHub Actions secrets and variables, GITHUB_TOKEN, OIDC tokens, AI service keys, and secrets held in the memory of the runner's processes. It also reads credential files for AWS, Azure, GCP, Kubernetes, npm, PyPI and SSH from their usual locations.

Then it uses what it finds to go deeper into cloud accounts. It exchanges GitHub OIDC tokens for AWS, GCP and Azure sessions, and on AWS it can create long-lived access keys for IAM users. Some steps are built to survive key rotation: rewriting an AWS role's trust policy so any AWS account can assume it, or adding federated credentials prefixed wfx-ci- to Azure AD applications so workflows can sign in without a stored secret.

In 7,309 repositories the stealer was added to an existing workflow. In 771 it arrived as a postinstall hook in package.json, which also runs on a developer's machine during npm install.

Check your repositories from Tuesday onward

For every repository your team or contractors can write to, look for these signs (taken from The Hacker News list):

  • Branches named wfx-docs, ci-check-NNNN, or short-lived chore/ branches with eight random characters.
  • A pull request titled docs: dependency audit notes.
  • Commits with messages such as ci: refresh build cache (adds roughly 1,500 lines to a workflow), chore: add ci helper (adds a .ci-xxxxxx.sh script and a postinstall hook), chore: add dependency audit workflow, ci: restore or ci: cleanup.
  • Unfamiliar workflow files such as .github/workflows/build-audit-xxxxxx.yml.

One useful tip: a missing run number on the attacker's commit is a more reliable sign that the stealer ran than searching for wfx- artifacts, because a repository can lose data without leaving any artifact.

You can start with this on a local clone to inspect history, including branches no longer visible in the UI:

git fetch --all --prune
git log --all --since="2026-11-10" --oneline \
  --grep="ci: refresh build cache" \
  --grep="chore: add ci helper" \
  --grep="chore: add dependency audit workflow"
grep -R "postinstall" package.json

This only matches the published commit messages. An empty result is not proof of safety, so also review your organisation's GitHub audit log.

If you were hit: the rotation order

Treat every secret and cloud role the workflows could read as compromised. SafeDep recommends:

  1. Revoke the commit author's tokens, OAuth app grants, SSH keys and sessions, and enable two-factor authentication.
  2. Rotate every secret and variable the repository can read.
  3. Treat any machine that ran npm install on an affected commit as compromised and rotate its keys.

Because the malware may leave backdoors that outlive rotation, also review the cloud side:

  • Entra ID: federated credentials named wfx-ci-* that you did not create.
  • AWS CloudTrail: AssumeRole or AssumeRoleWithWebIdentity sessions named wfx-*, followed by CreateAccessKey or UpdateAssumeRolePolicy.
  • GCP: GenerateAccessToken and SignJwt calls from your GitHub workload identity pool.
  • Vault: JWT logins from the GitHub OIDC issuer.

Reducing the blast radius next time

This wave shows that protecting accounts is not enough; assume a workflow will one day run code that is not yours. A few things to do this week:

  • Prefer OIDC with least-privilege, short-lived cloud roles scoped to repository and branch, instead of long-lived keys stored as secrets.
  • Set the default GITHUB_TOKEN permission to read-only and raise it per job only where needed.
  • Enable branch protection and required reviews for changes under .github/workflows/.
  • Separate secrets by environment (staging, production) and use environments with approvers for deployment steps.
  • Audit contractors and collaborators: who still has write access, and who has 2FA enabled.

We have written before about how GitHub handles leaked keys in GitHub secret scanning, and about the consequences of hardcoded secrets in the Wyndham data exposure (both in Vietnamese). The lesson is the same: attackers go wherever secrets live. If you outsource development, see how we work on the Besoft projects page.

A good first step this morning: pick your five most important repositories, run the check above, compare against the audit log, and write down which secrets each workflow reads. If that list is longer than you expected, it is time to cut it down.

Sources

  • The Hacker News, "Attacker Used 4,837 Hijacked GitHub Accounts to Run a CI Secret Stealer in 1,806+ Repositories", 12 Nov 2026: https://thehackernews.com/2026/11/4837-hijacked-github-accounts-ran-ci.html
  • SafeDep technical analysis: https://safedep.io/blog/github-actions-wfx-credential-stealer
  • Aikido Security technical analysis: https://www.aikido.dev/blog/compromised-github-accounts-push-ci-secret-stealer-to-tens-of-thousands-of-repositories