ADVISORYBrotli decoder flaw · check by handBrotli flaw · check by hand eMudhra certificate keys compromised · free checkeMudhra keys compromised · free check CL-61 · dfc2197 · Last deploy · 0 cookies · 0 third-party requestsLight-time to Deimos 11.4 min at
Sheet 4.09Series 4 · IncidentsA free guide from Deimos AI LLCIncident · GitHub Actions · since 2026-10-07Part of Wardlatch

Wardlatch · GitHub Actions · Check and fix · Free

GitHub ActionsWas a secret-stealing workflow planted in your repository?

Since 2026-10-07, hijacked GitHub accounts have committed credential-stealing workflow files to tens of thousands of repositories. A workflow you didn’t write can send your CI secrets, such as registry tokens and cloud keys, to an attacker. There is no version to update: list the workflow files added to your repositories since 2026-08-30, confirm someone on your team added each one, and if one is unknown, delete it and rotate every secret your Actions could read.

What to look forSince
  • New files in .github/workflowsadded by a commit you don’t recognise2026-08-31
  • Names reported in this campaignsecurity-audit.yml, github_actions_security.yml2026-08-31
From The Hacker News’s report, 2026-10-09 (updated 2026-10-15), which Wardlatch’s incident file cites, read 2026-10-25 (sources). A planted workflow can have any name. Wardlatch reads from 2026-08-30 00:00 UTC, so 2026-08-31 is covered in every time zone.
AHow to check

Check every workflow file added since 2026-08-30.

Run Wardlatch from your home folder (after cd ~) on the machine that holds your clones; it needs Node.js 18.17 or later.

Wardlatch · paste into a terminal
npx wardlatch@latest --incident github-actions-2026-10

Update, 2026-11-02 (0.1.11). A bare repository is now read too. If an earlier version said SAFE for this check, run it again. The changelog has every change.

Update, 2026-10-27 (0.1.10). If Wardlatch 0.1.0 to 0.1.9 said SAFE for this check, run it again: those versions could miss a workflow added since the campaign began, and 0.1.10 fixes that, and the same kind of miss in the committed-keys check (0.1.1 to 0.1.9), where a repository’s own settings could point git elsewhere (GHSA-96mq-627m-9qhg; what changed).

It finds the git repositories in the usual project folders (~/Development, ~/code, ~/src, ~/projects and the like, or the folders you give with --paths). In each, it lists every file in .github/workflows added by a commit dated on or after 2026-08-30 00:00 UTC, on any branch, tag or other ref in the clone. The date it goes by is the commit’s committer date. Wardlatch gives git no date: it reads the whole history and compares each commit’s time itself, so a back-dated commit can’t hide the commits beneath it. Each commit that added a workflow file gets its own REVIEW line, newest first, with the commit, its committer date in UTC and its author. A file added, deleted and added again is listed at each commit that added it, and so is one that only a merge adds. Review lines can’t tell a good workflow from a bad one, and they don’t change the exit code. With nothing to list, the SAFE line names the start: “no workflow files added in commits dated on or after 2026-08-30 00:00 UTC (committer date) in N local repositories”. It reads the history with git log, with the repository’s own settings kept from running any program, fetches nothing and changes nothing.

A repository reads UNKNOWN, never SAFE, when Wardlatch can’t read all of it. A shallow clone is UNKNOWN wherever its history is cut, and so is a full repository with a shallow file: run git fetch --unshallow in it, then run Wardlatch again. In CI, check out with fetch-depth: 0: actions/checkout fetches one commit unless told otherwise. A repository with a commit whose time git can’t read is UNKNOWN too. So is a repository where git log doesn’t complete, takes over 20 seconds (“git did not finish in time”) or writes more than can be read. Reading the whole history takes time: on the maintainer’s Mac, about 0.5 seconds for 100,000 commits and 7 seconds for 1.56 million.

By hand, one repository at a time: paste this line at the repository’s top folder, in a POSIX shell such as bash or zsh. It doesn’t run as pasted in PowerShell or cmd. First run unset GIT_DIR: a GIT_DIR set in your shell points git at that repository instead (a GIT_COMMON_DIR or GIT_OBJECT_DIRECTORY left set makes git stop with an error). Then run git rev-parse --is-shallow-repository: if it prints true, run git fetch --unshallow. Two more things the line can’t get past: a commondir file in .git makes git read the history of the repository it names instead (ls .git/commondir shows one; Wardlatch reads that repository UNKNOWN), and where .git is a file, not a folder, git reads the repository it names, whichever that is (cat .git shows which; Wardlatch follows it too). It has no date in it, and it reads what Wardlatch reads, in one command. It lists every workflow file that any commit on any branch, tag or other ref added, whatever the date. Each file is under the line of the commit that added it: the short hash, the commit time in seconds since 1970, the committer date in UTC and the author. A file added, deleted and added again is listed under each commit that added it. A commit line with no file under it (often a merge) added no workflow file. As in Wardlatch, paths are read from the top of the folder it runs in (--work-tree=.), whatever the repository’s own core.worktree setting says, a replace ref doesn’t put another commit in a commit’s place, a commit-graph file isn’t used, the side branches merges bring in are read, and nothing the repository’s config names is run: no signature check, no pager and no download.

By hand · at a repository’s top folder
TZ=UTC GIT_NO_LAZY_FETCH=1 GIT_ALLOW_PROTOCOL=none git --no-pager --work-tree=. --no-replace-objects -c core.commitGraph=false -c advice.graftFileDeprecated=true log --all --full-history --root --no-renames --diff-merges=combined --diff-filter=A --name-only --no-show-signature --date=iso-strict-local --format='%h %ct %cd %an' -- .github/workflows
Review
The files under every commit line whose second field, the commit time in seconds, is 1788048000 or more, or isn’t a plain number. 1788048000 is the start, 2026-08-30 00:00:00 UTC. Compare that number and ignore the date text: for a commit time far in the future, git prints 1970-01-01T00:00:00Z or even a negative year, and those commits are in the window too. A line with no number and no date (three spaces after the hash) has a committer date git can’t read: count it as in the window as well. Open each of those files and confirm someone on your team added it, and look at any other you don’t recognise: a commit can state any date.
Unknown
A workflow nobody on your team added, or one of the names above: follow section C.
Not checked
A line that starts fatal: means git stopped before the end, so the repository wasn’t fully checked, for example outside a repository, or in a partial clone that lacks what the line needs (Wardlatch reads that one UNKNOWN). A line that starts hint: Support for <GIT_DIR>/info/grafts is deprecated means the repository has a grafts file: git follows it, and no option turns that off, so the list can’t be trusted (Wardlatch doesn’t read the file). In a shallow clone, what is below the cut isn’t there to list: run git fetch --unshallow first.
Nothing
No output, when run at the repository’s top folder in a full clone (git rev-parse --is-shallow-repository prints false), where ls .git/commondir finds no file and .git is a folder: no workflow file was ever added on any branch, tag or other ref this clone has. If .git is a file, cat .git shows which repository git read (or the folder whose commondir file names it): no output means only that that repository has none. If git rev-parse --is-shallow-repository prints true, run git fetch --unshallow first: in a shallow clone, a workflow below the cut can show the cut commit’s date, or not show at all.
BFixed versions

There is no version to update.

This isn’t a flaw in a program you can patch: the attackers commit with accounts they took over. What protects you is knowing every workflow in your repositories, and rotating secrets when one is unknown.

CIf you find one

Delete it, rotate, review.

  1. Delete the workflow from every branch, not only the default one.
  2. Rotate every secret the repository’s Actions could read: registry tokens, cloud keys and the rest. Assume they were sent.
  3. Revoke the account’s access. The commit came from a GitHub account: review its recent commits, and revoke and replace its credentials if it’s one of yours.
  4. Check forks and mirrors of the repository, private ones included: they may carry the same workflow.
  5. Don’t wait for abuse. As StepSecurity says in its own report, the absence of abuse so far is not evidence the credentials are safe.
DLimits

What the check doesn’t see.

  • Only what your clone has. Branches on GitHub that your clone hasn’t fetched aren’t seen.
  • Commit dates can lie. Whoever makes a commit sets its date. Wardlatch doesn’t list a workflow whose own commit is dated before 2026-08-30. The command by hand lists every date, so look past the date at any workflow you don’t recognise.
  • Repositories it finds. It searches four folders deep, skips folders whose names start with a dot (and node_modules, .venv, venv), and doesn’t look inside a repository for another one; use --paths for clones elsewhere. --paths replaces the usual project folders, so name them too if they hold clones, such as --paths ~/Development --paths ~/clones.
  • Changed workflows. Both checks list added files, as the campaign added them. A change to a workflow file that already existed isn’t listed: a commit that only changes a workflow committed before 2026-08-30 reads SAFE.
  • A history cut short. A shallow clone, or a shallow file in a full repository, hides what is below the cut. Wardlatch reads such a repository UNKNOWN; the command by hand lists only what is above the cut. Run git fetch --unshallow first.
  • A grafts file. A .git/info/grafts file makes the command by hand unreliable. Wardlatch doesn’t read it.
  • A .git file, and write access to .git. Where .git is a file naming another repository, Wardlatch and the command by hand read that one, whichever it is. Whoever can write to a repository’s .git folder can hide a workflow anyway, for example by deleting the branch that leads to it.
  • Your files, not GitHub’s settings. It doesn’t look at your secrets, your account or GitHub’s audit log.

This page describes Wardlatch 0.1.13 and its incident file, github-actions-2026-10.json.

ESources

Sources and credits.

  1. The Hacker News, credential-stealing GitHub Actions workflows, 2026-10-09, updated 2026-10-15 (the report Wardlatch’s incident file cites): activity since 2026-10-07; more than 500 accounts and tens of thousands of repositories; a related wave from 2026-08-31 to 2026-09-30; the reported file names; what repository owners should do.
  2. Wardlatch 0.1.13, github-actions-2026-10.json, workflows.mjs, README and CHANGELOG: the instant it lists from, how it reads the history, and the command by hand.

Credits · The report draws on StepSecurity, who identified the hijacked accounts and the pushes; Socket, who measured the campaign across more than 500 accounts; and GitGuardian, who reported the August and September wave. All sources read 2026-10-25.

FQ&A

Debrief.

The questions this page answers, every answer open.

Q-01

What are the planted GitHub Actions workflows?

Since 2026-10-07, hijacked GitHub accounts have committed credential-stealing workflow files to tens of thousands of repositories. A workflow you didn’t write can send your CI secrets, such as registry tokens and cloud keys, to an attacker (The Hacker News).

Q-02

How do I find a planted workflow in my repository?

List every file in .github/workflows that a commit added, on every branch, and confirm someone on your team added each one dated 2026-08-30 or later, and any other you don’t recognise: a commit can state any date. The check has a one-line git log command to paste at the top folder of a repository, with no date in it, that lists every workflow file ever added under its commit’s time in seconds: review the files under every line whose number is 1788048000 (2026-08-30 00:00:00 UTC) or more, or isn’t a plain number, and count a line with no number and no date as in the window. First run unset GIT_DIR, so a GIT_DIR set in your shell can’t point git at another repository, then git rev-parse --is-shallow-repository: if it prints true, run git fetch --unshallow. No output means no workflow file was ever added only in a full clone where ls .git/commondir finds no file and .git is a folder; if .git is a file, cat .git shows which repository git read (or the folder whose commondir file names it), and no output means only that that repository has none. In every repository it finds, Wardlatch 0.1.13 lists the workflow files added by commits dated on or after 2026-08-30 00:00 UTC, one REVIEW line for each commit that added one, newest first. It gives git no date and reads the whole history, so a back-dated commit can’t hide the commits beneath it, and a shallow clone reads UNKNOWN: run git fetch --unshallow first.

Q-03

Which file names were reported?

The report names security-audit.yml and github_actions_security.yml. A planted workflow can have any name, so check every workflow you didn’t write.

Q-04

I found a workflow nobody on my team added. What now?

Delete it from every branch, rotate every secret the repository’s Actions could read, and review the account that committed it: revoke and replace its credentials if it’s one of yours. Check forks and mirrors of the repository too.

Q-05

Does Wardlatch check GitHub Actions?

It lists, it doesn’t judge. npx wardlatch@latest --incident github-actions-2026-10 finds the git repositories in your project folders and, in each, lists every workflow file added by a commit dated on or after 2026-08-30 00:00 UTC (the committer date), as REVIEW lines, which don’t change the exit code. Wardlatch gives git no date: it reads the whole history and compares each commit’s time itself, and lists each commit that added a workflow file, newest first. A shallow clone, a commit time git can’t read, or git log not completing within 20 seconds reads UNKNOWN, never SAFE; in CI, check out with fetch-depth: 0. It reads only what your clones have. It doesn’t list a workflow whose own commit is dated before 2026-08-30, or a change to a workflow file that already existed. Wardlatch 0.1.0 to 0.1.9 could miss a workflow added since the campaign began, and 0.1.10 fixes that, and the same kind of miss in the committed-keys check (0.1.1 to 0.1.9), where a repository’s own settings could point git elsewhere: if one of them said SAFE here, run it again.