Frontend
Two and a half months with an intruder in our repositories
Biya Paul DEV Community
2 views
On 6 September 2026, an antivirus quarantined a 32 KB "font file" in one of our projects. That file was a trojan. It had been running on our workstation since 22 June, had rewritten commits in eighteen repositories, and had spread to a collaborator's machine. Here is what happened, what we did, and what we are keeping from it.
An ordinary morning, two details that did not add up
It started with two observations that, taken separately, would have worried nobody. On opening the machine, every one of our repositories showed a pending pull we could not account for. And every one of them had triggered an automatic deployment on GitHub overnight, which had failed.
An hour later, the antivirus reported that it had quarantined a file named fa-solid-400.woff2 in the public/fonts folder of a marketing site. A Font Awesome font. Except that a font by that name weighs more than 100 KB and starts with the binary signature wOF2. This one weighed 32,874 bytes and started with several hundred spaces, followed by unreadable JavaScript.
A real font starts with "wOF2". The fake one starts with spaces, then code.
What we found when we dug
We spent the afternoon examining, read-only, our whole workspace: about sixty repositories. The picture that came out was far wider than the one flagged file.
Eighteen repositories affected, across three GitHub organisations, including client projects.
A first injection on 22 June, in a marketing site. Seventy-seven days before detection.
A signature in the git history: in every affected repository, the last commit had been copied verbatim, same message, same author date, but with a truncated committer name and a -0700 timezone, the US west coast. Those copies had been force-pushed with our own SSH key.
A collaborator's workstation carried the same signature from late August: they had cloned an already infected repository.
A last malicious push the day before, at 15:21. The malware was still active while we were looking for it.
Seventy-seven silent days, then fifteen projects hit in ten days.
The mechanism itself fitted in three files. A .vscode/tasks.json declared a hidden task that ran the fake font with Node.js as soon as the folder was opened in VS Code. A .vscode/settings.json enabled automatic tasks so the editor would not ask. And in one variant, the same code was appended after a long run of spaces at the end of tailwind.config.js, vite.config.js or eslint.config.mjs, to run on every build, including in our pipelines and Docker images. We go through all of it in a second, more technical article.
What the malware had within reach
A trojan of this kind is not trying to break anything. It is after credentials. For two and a half months it had access to everything on the workstation of an independent developer working for several clients:
SSH keys, without a passphrase, that opened both GitHub and the production server;
the .env files of thirteen projects, with their payment, AI, email, storage and database keys;
passwords saved in the browser and open sessions;
deployment secrets, exposed in the pipelines its own pushes had triggered.
None of that was encrypted by anything other than the Windows session. That is the hardest lesson of this story: cleaning the machine is only the beginning. The real remediation is making everything that was copied worthless.
What we did, in order
1. Cut off access
Before anything else, from a phone rather than the affected workstation: delete every SSH key on the GitHub account, change the password, sign out all sessions, revoke access tokens and authorised applications. Without a key, the malware cannot push anything, wherever it is.
2. Clean the workstation
We chose an in-place clean-up over a reinstall, for lack of a backup drive that day, and accepting the residual risk. Quarantine of the files dropped in the temp folder, removal of stored keys and credentials, deletion of the malware's files and decoys across the eighteen repositories, purge of forty gigabytes of installed dependencies, hardening of VS Code, then a full re-test: no marker left, no process, no persistence, no suspicious connection.
3. Regenerate every secret
This is the longest job. We inventoried the variable names of twenty-seven .env files and the secrets of nineteen pipelines, then applied the same rule to each: create the new one at the provider, put it in place, verify, and only then revoke the old one. In that order, otherwise the application goes down while everything is put back.
4. Audit the server
SSH login logs since June, authorised keys, users, scheduled jobs, services, Docker images built during the period. A new deployment key, separate from the personal key. Rebuild of every image from the cleaned repositories.
5. Clean the repositories and tell people
Removal of the malware from the branches still infected on GitHub, force-push protection, mandatory signed commits, a guard workflow that fails if a signature reappears. Then a message to every collaborator and every client whose secrets had been on our workstation. Saying it quickly protects the relationship; staying silent exposes it.
What we are keeping
The antivirus did not save us. A failed push and an unexplained pull did.
VS Code runs code when you open a folder. The task.allowAutomaticTasks setting exists for that, and the malware set it to on in every project. We now force it to off in our user settings, with workspace trust enabled. Opening an unknown repository is not a harmless gesture.
One key for everything is one leak for everything. The same SSH key, without a passphrase, opened GitHub and production. Today: a personal key with a passphrase for GitHub, one deployment key per server that lives only in the pipeline's secrets, and never one in place of the other.
npm install scripts are a way in. ignore-scripts=true in ~/.npmrc costs a few pnpm rebuild a year and closes an entire class of attacks.
Git history is a free audit log. A single command, run weekly, would have revealed the rewritten commits on 22 June. We now run it every Monday:
git log --all --format='%h|%ad|%cd|%an|%cn|%s' --date=iso \
| awk -F'|' '$3 ~ /-0700/ || $4 != $5' | grep -v GitHub
An incident is handled with a plan, not with adrenaline. We wrote ours down as four procedures: detect and remove on a workstation, on a server, in the repositories, then the actions to take after an infection. They were useful the very next day, to walk the affected collaborator through the same steps.
If you build with Node.js and VS Code
Do these four things this week. They take less than an hour.
In VS Code: task.allowAutomaticTasks set to off, security.workspace.trust.enabled checked.
In ~/.npmrc: ignore-scripts=true.
A passphrase on your SSH keys, and a deployment key separate from your personal key.
Search your projects for a fa-solid-400.woff2 file of 32,874 bytes, and a tasks.json containing folderOpen. If you find one, do not open it: read the technical analysis and start by cutting off your access.
This campaign targets developers, often through fake job interviews or fake freelance assignments that come with a repository "to run". We have not yet identified our own entry point. If you have been through the same thing, write to us: comparing traces helps everyone.
Read original: https://dev.to/bpsmartdesign/two-and-a-half-months-with-an-intruder-in-our-repositories-4a69
← Previous
Free Rails Architecture Checkup: run it in Codex, Claude Code, Cursor, etc. It shows where your app gives agents conflicting instructions, hides key architecture, or leaves risky decisions up to guesswork.
https://railsbaseline.com/checkup/try/
Next →
Why Client-Side Tracking Fails in Fintech (And How to Implement Meta CAPI for Funded Accounts)
Related
Preparing a Markdown document for a technical slide deck with Gamma App
Frontend
1
DEV Community
Upgrade Skill Development Kamu dengan Menjelajahi Fitur Keren di Tencent EdgeOne Makers
Frontend
1
DEV Community
12 Pitfalls I Hit Auto-Logging Claude Code Subagents with a Stop Hook (and How the Numbers Cut My Weekly Cost 15–20%)
Frontend
3
Dev.to (EN Zone)
React Server Components para veteranos de SPA
Frontend
5
Dev.to (EN Zone)
Comments0
No comments yet — be the first