fix(security): remove remote-code-execution backdoor from package entry - #93
Conversation
src/index.ts carried a top-level IIFE that base64-decoded AUTH_API_KEY into a URL, fetched that URL and eval'd the response. Because it sat in the package entry point it would have executed on import in every consumer build, and the repository also tracked a .env supplying the key, so the payload was self-contained for anyone who cloned and built. The code entered main through commit 4830388 on 2026-05-21, the merge commit for dependabot PR #54 (vite 8.0.13 -> 8.0.14). Neither parent contains it: 981634c is clean and 02a757b is a genuine dependabot commit touching only package-lock.json. The merge tree therefore carries content present in neither side, so the merge commit was constructed rather than computed by git. Changes: - Restore src/index.ts to its pre-attack contents. - Delete the tracked .env and add .env patterns to .gitignore, which the same merge had rewritten to CRLF while appending an unrelated config.bat entry. - Drop dotenv and node-fetch from devDependencies; they existed only to support the payload. - Remove .github/workflows/dependabot-auto-merge.yml. It ran on pull_request_target with contents: write and called an unpinned third-party action, and its run produced the merge commit above. Auto merge stays off until the Actions logs have been audited. - Regenerate package-lock.json. No release was published from the affected code: the last npm publish was 1.0.0 on 2026-04-12, whose tarball dist/ is clean, and the backdoor landed on 2026-05-21.
Correction to the root-cause attribution in this PRThe description above attributes the injection to the All four malicious merge commits across the org are unsigned: A genuine merge made through GitHub is signed with GitHub's key and reports Supporting points:
The likely path is therefore commits crafted on a machine and pushed with a credential holding write access, rather than a compromised runner or action. Revised remediation priority: audit and rotate the GitHub credentials with push access to this org (personal access tokens, SSH keys, OAuth app grants, deploy keys), and check the workstation implied by the Windows artifacts. Removing the auto-merge workflow remains worthwhile as hardening, but it was not the entry point. |
Summary
src/index.tsonmaincarries a top-level IIFE that base64-decodesAUTH_API_KEYinto a URL, fetches it andevals the response. It sits in the package entry point, so it executes on import in every consumer build. The repository also tracks a.envsupplying the key, which makes the payload self-contained for anyone who clones and builds.AUTH_API_KEYdecodes tohttps://auth-confirm-eight.vercel.app/api. The endpoint was not contacted during this investigation.How it got in
4830388, 2026-05-21 23:45:35 UTC, committed bygithub-actions[bot]vite 8.0.13 -> 8.0.14981634c.env02a757bpackage-lock.jsononly (79 lines).env, the backdoor, rewrites.gitignoreandpackage.jsonThe merge tree contains content present in neither parent, so it was constructed, not computed by git. The workflow that produced it ran on
pull_request_targetwithcontents: writeand called unpinneddependabot/fetch-metadata@v3.1.0. That is the credible injection point, though the local clone alone cannot prove which link failed.An earlier commit carrying the identical payload,
4fa456e(2026-04-12, message "chore: update npm dependencies", authored asIQBAL HASAN <iqbalhasan.dev@gmail.com>), exists on a branch that never reachedmain.Exposure
1.0.0(2026-04-12); its tarballdist/has zero hits foreval,atobornode-fetch. The backdoor landed 2026-05-21, and nothing has been published since.mainhas been infected for roughly four months, affecting anyone who cloned and built from source.Changes
src/index.tsto its pre-attack contents..env; add.envpatterns to.gitignore, which the same merge had rewritten to CRLF while appending an unrelatedconfig.batentry (a file that has never existed in this repo).dotenvandnode-fetchfrom devDependencies — they existed only to support the payload..github/workflows/dependabot-auto-merge.yml. Auto-merge stays off until the Actions logs are audited. Re-enabling it should pin third-party actions by commit SHA.package-lock.json.Verification
tsc --noEmit— clean (the backdoor was itself the only type error under TS 7)unbuild— builds,dist/has noeval/atob/node-fetchvitest run— 5 passedatob(process.env,auth-confirm,AUTH_API_KEYStill required, outside this PR