Skip to content

Security incident: compromised account force-pushed malware to RMG repositories (2026-09-06) #3008

Description

@rwest

This issue text written with Claude

Summary

On 6 September 2026, between 04:37 and 04:59 UTC, a collaborator's GitHub account was used — without their knowledge, while they were asleep — to force-push malicious commits to 62 branches across 8 repositories in 4 organizations. The account had been compromised by credential-stealing malware.

All 62 branches have been restored to their verified pre-attack commits. No default branch of RMG-Py, RMG-database, RMG-website or conda-recipes was ever affected. No release, tag, or published package was altered. If you only ever used main, our repositories never exposed you.

But please don't stop reading there. The malware that reached us is part of a much wider campaign that is still active, and it spreads through channels that have nothing to do with RMG. The detection steps below are worth running whether or not you touched an affected branch.

What the malicious commits contained

Each push replaced a branch tip with a forged commit — same message and parents as a real merge, spoofed to show a genuine maintainer's name, but unsigned — adding these files:

File Purpose
public/fonts/fa-solid-500.woff2 Not a font. A JavaScript dropper, whitespace-padded to look empty, that fetches and executes remote code.
.vscode/tasks.json Runs that file automatically on "runOn": "folderOpen", with output hidden.
.vscode/settings.json Sets "task.allowAutomaticTasks": true to suppress VS Code's confirmation prompt.
.gitignore Modified to remove the .vscode ignore rule so the payload gets committed.
public/fonts/fa-*.{eot,ttf,woff,woff2,svg} Genuine FontAwesome files, present as camouflage.

The trigger is opening the repository folder in VS Code. Not building, not testing, not running anything. Cloning alone does nothing.

⚠️ Important: because the injected settings pre-suppress the "allow automatic tasks?" prompt, you would not have been asked to allow anything. "I never clicked Allow" is not evidence you're safe. And since these repos were ones you already trust, nobody would hesitate over the Workspace Trust dialog for their own RMG clone.

The payload steals credentials — tokens, SSH keys, browser sessions, crypto wallets — then spreads by pushing itself to every repository those stolen credentials can write to. That is how it reached us.

Affected RMG-area repositories

  • ReactionMechanismGenerator: RMG-Py (13 branches, incl. gh-pages), RMG-database (14), RMG-website (10, incl. dev), conda-recipes (2), SoftwareCoordination (1, main)
  • comocheng: ChemCheck, gpuhackathon2023, meOH-analysis (9 total)
  • CHME5137: KMC (11)
  • pr-omethe-us: ChemKED-database (2)

Six were default branches. All have been restored.

This is part of a wider campaign

We are not the target — we were collateral. At the time of writing, roughly 50 unrelated public repositories carry this same payload on their default branches: e-commerce sites, Laravel and Flutter apps, Web3 projects. The pattern is already documented publicly by other developers who received it directly.

The initial infection vector is a fake job interview. Publicly documented cases describe:

  • A LinkedIn or email approach from a "recruiter", "investor" or "hiring manager" at a plausible-looking company
  • A professional company deck, salary discussion, a Calendly link
  • A "small coding task" repository you're asked to clone and run before the interview
  • Pressure to run it and send screenshots; the interviewer joins with no camera or voice, or reschedules

Developers in crypto/Web3 are targeted most, but the lure is generic. Opening that task repo in VS Code is all it takes.

If you have been approached this way recently, or completed any coding-task repository from a recruiter, treat your machine as compromised and follow the cleanup below — regardless of whether you ever touched an RMG repository. This has fooled experienced engineers; there is nothing embarrassing about being targeted.

Am I at risk? — check your whole machine, not just RMG clones

Run these against your home directory. They cover any repository from any source, which is what matters.

1. Find fake fonts that are actually code. A genuine .woff2 is binary; the dropper is text. This is the highest-signal check and has almost no false positives:

find ~ -name '*.woff2' -type f -print0 2>/dev/null | xargs -0 file 2>/dev/null | grep -v 'Web Open Font'

Any .woff2 reported as ASCII/Unicode text is a dropper. (Also treat fa-solid-500.woff2 as suspicious on its face — FontAwesome's solid weight is 900; a "500" solid font does not exist.)

2. Find VS Code tasks that auto-run on folder open:

grep -rl --include=tasks.json 'folderOpen' ~ 2>/dev/null

3. Find workspaces that suppress the auto-task prompt:

grep -rl --include=settings.json 'allowAutomaticTasks' ~ 2>/dev/null

4. Check whether the payload ever entered a repo's history (run inside any clone you're unsure about):

git log --all --oneline --diff-filter=A -- 'public/fonts/*' '.vscode/tasks.json'

A hit on any of these means the payload reached your disk. That is not the same as infection — the question is whether VS Code ever opened that folder. If you cannot rule it out, assume it did.

If you find it on your machine

If the folder was never opened in VS Code, clean up and carry on:

git fetch --all --prune
git checkout <branch> && git reset --hard origin/<branch>
git clean -fd .vscode public/fonts

Otherwise, treat the machine as compromised. Do all of this from a different device:

  1. github.com/settings/tokens — delete every personal access token
  2. github.com/settings/keys — delete every SSH and GPG key
  3. github.com/settings/applications — revoke every OAuth app
  4. github.com/settings/security — sign out of all other sessions, change your password, enable 2FA
  5. Rotate every npm / PyPI / anaconda / cloud / CI credential that machine held
  6. Change any password saved in that browser
  7. Move any crypto wallet funds to a new wallet with a new seed phrase — this malware family specifically targets wallets

Then rebuild the machine. The dropper executes whatever the remote server sends, so you cannot enumerate what was installed. If a rebuild truly isn't possible today, at minimum delete every .vscode/tasks.json, .vscode/settings.json and public/fonts/ copy on disk before opening any repo in VS Code again — but understand that this removes the dropper, not whatever it already installed.

If you have a fork that received a poisoned branch, restore it so you don't re-infect others. GitHub records the pre-attack commit:

gh api "repos/YOU/REPO/activity?time_period=week" \
  --jq '.[] | select(.timestamp >= "2026-09-06") | "\(.ref) \(.before) \(.after)"'

gh api --method PATCH "repos/YOU/REPO/git/refs/heads/BRANCH"
-f sha=THE_BEFORE_SHA -F force=true

If that returns 404 despite correct permissions, GitHub no longer holds that object in that repository — push it from a local clone instead:

git push --force origin ${sha}:refs/heads/${branch}

Two gotchas we hit: only reset a branch still sitting on the malicious commit (investigate anything that moved), and in zsh use the ${sha} braces or the refspec gets mangled into a confusing "src refspec does not match any".

Reducing the risk of this class of attack

In your editor — this is what actually stops it:

  • Keep Workspace Trust enabled, and open unfamiliar repositories in Restricted Mode. It exists for exactly this.
  • Set "task.allowAutomaticTasks": "off" in your user settings, so a workspace can't turn it on.
  • Treat .vscode/ as executable content. A repository can run commands the moment you open it.
  • Never open a stranger's repository on your main machine. Use a disposable VM or GitHub Codespaces, and click Deny on any automatic-tasks prompt.

Reviewing code:

  • Scrutinise any diff touching .vscode/, .gitignore or .github/workflows/, especially alongside unrelated changes.
  • Be suspicious of binary "assets" in a source-only project. This payload hid in a .woff2.
  • On a fresh clone, git log --stat -1 costs seconds.

Accounts and repositories:

  • Enable 2FA, and enforce it org-wide.
  • Prefer short-lived, narrowly-scoped tokens over long-lived PATs.
  • Require signed commits on important branches. Every malicious commit here was unsigned while every legitimate merge was verified — that single check separates them cleanly.
  • Add rulesets blocking force-pushes.
  • Watch your repository activity feed: gh api "repos/OWNER/REPO/activity?time_period=week" shows every ref change with before/after commits. This is how we reconstructed the incident.
  • Consider a CI scanner for this pattern; 187N-ai/malware-scan-action detects it.

What we did

  • Reconstructed every ref change from the repositories' activity logs, cross-checked against an independent local clone snapshot and the Slack notification history.
  • Confirmed the injection was confined to the files listed above. No workflow file was modified, no CI was weaponised, and no default branch of RMG-Py, RMG-database, RMG-website or conda-recipes was touched.
  • Restored all 62 branches to their verified pre-attack commits.
  • Revoked the compromised account's access and audited for persistence — no deploy keys, no new webhooks, no new app installations.
  • Verified no third-party pull request carries the payload.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions