TECHDEV · Flagship · Open-source tool

gheist-guard

A local, deterministic, read-only behavioral detector for the Gheist planted-workflow attack shape: one command, one verdict per repository, one rotate checklist.

TypeScript · Zero runtime dependencies · MIT · Node 20+ · Read-only by default · No model, no account, no secret values ever printed.

Not the Pokémon move or the Elder Scrolls spell: this gheist-guard is a local CLI detector for the Gheist planted-workflow campaign Wiz reported (~2026-10-26).

The attack it watches for

A token stolen from one developer machine becomes an organization-wide secret dump with no strange domain and no outbound connection for a firewall to catch. The attacker pushes a branch named ci-sync- plus six characters, commits a workflow that prints repository secrets base32-encoded into the run log (GitHub masks secret values only when they appear verbatim), then downloads the log through the GitHub API and deletes the run within seconds. Wiz Research recovered more than 13,500 planted branches from more than 850 compromised accounts in its October 2026 reporting. GitHub's July 28, 2026 control holds suspicious runs in public repositories for collaborator approval, but it does not cover private repositories or GitHub Enterprise Server — exactly where this attack succeeds. Deleting the branch or the run does not undo the exposure: every secret the run could read must be rotated.

“Gheist” is the name Wiz Research gave to the campaign it reported in October 2026. This tool is not affiliated with, endorsed by, or connected to Wiz, and it does not claim to be the first to detect any campaign. A dump workflow contains no secret text, so no content scanner flags it — gheist-guard watches the act, from the evidence that survives it.

What it checks

RuleEvidence
R1 — Branch patternBranch matches ^ci-sync-[0-9a-z]{6}$
R2 — Workflow identity.github/workflows/sync-check.yml with commit “ci: sync config”
R3 — Dump contentBase32/base64 secret-dumping content in the workflow
R4 — Run lifecycleRun completed, then deleted within 60 seconds
R5 — Tool fingerprintGitHub API user agent “gheist”
R7 — Self-hosted variantJob named sync-host hunting credentials on the runner

Full rule reference: docs/RULES.md in the repository.

Verdicts

VerdictMeaningExit code
CLEARNo rule fires.0
SUSPECTA rule fires, but the full documented combination is not present. Carries the exact next check to run — never a rotation demand, never silently CLEAR.1
CONFIRMEDBranch pattern (R1) plus any of workflow identity (R2), dump content (R3), run lifecycle (R4), tool fingerprint (R5), or the self-hosted variant (R7). Always carries the ordered rotate checklist.1

Exit code 2 means a usage or API error. One stderr summary line is always printed. The name “Gheist” appears in a verdict only when the full documented pattern matches.

Install from source

git clone https://github.com/techdevfun/gheist-guard.git
cd gheist-guard
npm ci
npm run build
node dist/cli.js scan <owner/repo>

Commands copied exactly from the gheist-guard README (v0.1.1). Do not use npm install -g gheist-guard yet — the README lists it as “once published to npm”. Requires Node 20 or later. A fine-grained token with repository read scope is enough for the REST path; it may come from --token or GITHUB_TOKEN.

Examples

# Scan one repository (REST path, read-only)
node dist/cli.js scan <owner/repo>
# Scan every repository in an organization (REST path)
node dist/cli.js scan --org <org>
# Audit-log path (organization-owner token required for live use;
# --events runs it against recorded audit-log payloads)
node dist/cli.js audit --org <org> --events audit-events.json
# Re-print the rotate checklist from a saved report, without re-scanning
node dist/cli.js scan <owner/repo> > report.json
node dist/cli.js checklist <owner/repo> --report report.json

Legitimate automation can be allowlisted and is reported as SKIPPED with a reason, never silently dropped (see the README for --allow-branch-pattern and --allow-workflow).

The rotate checklist (CONFIRMED only, in order)

  1. Revoke the pushing token.
  2. Rotate every secret name the run could read.
  3. Check OIDC roles assumable from the matched ref.
  4. Check the pushing machine for infostealer compromise.

The checklist lists secret names only. The tool never prints, stores, or transmits a secret value, and v1 never deletes, rotates, or revokes anything — the operator does it.

Measured numbers (README, build 2026-10-27)

What this is NOT — and its limits

Source + README on GitHub → · Report an issue → · ask@techdev.fun

Related tools

More open-source tools from TECHDEV: sessguard, runverdict, vetgate, context-diet, ci-failure-detective, agent-seatbelt, devsecops-os, env-doctor, skill-audit, logtrim, testguard, breakcheck, keymap, memgraph, ggufit, leaseguard, factfence, jsonfold

Have a say

Vote for this tool, suggest a feature, or discuss it with other operators on the community board.