Skip to content
PublishedSoftware Supply Chain Security · v1.0 · 6 min

The AUTH_API_KEY Loader in 649 GitHub Repositories

An environment variable that conceals a URL, dynamic execution and overlapping affected accounts.

Cristian Chiriac

Published:
2 November 2026
Version:
1.0
Last observed:
30 October 2026
Dataset frozen:
1 November 2026
Collection cut-off:
31 October 2026, 17:59 UTC
Tracked items rechecked:
1 November 2026, 18:23 UTC
Time zone
All dates UTC.
Contents

Server and build files decode an environment variable and pass code to eval or Function.

The research in 30 seconds

What I found
Server and build files decode an environment variable and pass code to eval or Function.
What this adds
The pinned-commit set and decoded host inventory quantify a publicly described pattern; 193 owners also appear in the PolinRider set.
Verified scope
649 proven repositories under 379 accounts on 30 October–1 November, after a paged search, a review with fixed rules and the 31 October and 1 November additions; the URL decode set was collected on 28–30 October.
What remains unknown
The final payload was not retrieved, and the fetch step was not confirmed file by file.

The numbers, with their boundaries

Current count

649

Proven repositories

30–31 Oct 2026 · UTC

Counts:
Pinned-commit decode-and-evaluate rule; 379 owner accounts.

Earlier snapshot or subset

571 / 609

Decoded URL set

28–30 Oct 2026 · UTC

Counts:
571 URL-bearing .env values among 609 of the 614 proven repositories read.

Context · separate measure

193 / 379

Owners also in PolinRider

31 Oct 2026 · UTC

Counts:
Overlap of owner ids; shared affected accounts do not prove shared operators.
Technical detail:Long tables, code and the evidence register are folded; nothing is left out.

In 649 public repositories, an ordinary-looking server or build file decodes an environment variable named AUTH_API_KEY and evaluates code with it. The variable sits in the repository's .env file. It is not an API key: it is the base64 form of a web address.

  • The 649 repositories belong to 379 accounts. None is a fork.
  • 193 of those 379 accounts also own a proven PolinRider repository. The two loaders often hit the same victims.
  • In 571 of the 609 repositories whose .env I read, the variable pointed to one of 30 Vercel apps, most named auth-confirm-<word>, or to a file on the catbox file host.
  • The pattern itself was described publicly in 2025, in a recruitment-scam write-up. I found no public count of affected repositories and none of these addresses.

I did not request any of these addresses. The variable was decoded as text inside an isolated virtual machine. The owners are treated as victims and are not named.

Where this stands (1 November 2026)

#

The proven set was closed on 1 November 2026 with what had been found up to 31 October 2026, 17:59 UTC; repositories found after that are tracked separately and are not in these figures. The full proof pass at pinned commits ran on 30–31 October. On 1 November 2026, 18:23 UTC a read-only GitHub API recheck covered the 162 repositories, 73 accounts and 12 issues that are tracked one by one (operator repositories, Shai-Hulud accounts, phishing issues), not the whole set.

  • Frozen: 649 repositories under 379 accounts; the last three were added on 1 November after being pushed on 31 October, so the loader is still being planted.
  • Overlap: 193 of the owners also hold a proven PolinRider repository.

Key findings

#

How it works

#
From npm run dev to remote code
  1. 01A normal-looking fileT1059.007server.ts, index.ts, vite.config.ts, next.config.ts or a similar file that runs on start or build
  2. 02A secret that is not a secretT1027.013AUTH_API_KEY in the committed .env holds a base64 string
  3. 03Decodeatob turns it into a URL on a Vercel app or a catbox file
  4. 04EvaluateT1105In the public 2025 example, the code fetches the URL and passes the response to eval, so the payload can change without a commit

Because the address lives in .env, a reviewer who reads only the source sees a call that looks like authentication. The proof rule checks the decode-and-evaluate pattern; I did not confirm the fetch step file by file, and I never requested the addresses.

How the count grew on 30 October

#

The first proof pass found 231 repositories. They came from a single code search on 27 October that I did not page through. On 30 October the daily spread tracker ran the same search with full paging and found 447 more repository files that match the proof rule. I reviewed them with rules fixed before counting:

Review result Repositories Decision
Same file as a proven repository, or .env decodes to an address already in the proven set 352 Added
Same code structure as a proven file, or an auth-* Vercel address 32 Added
Same pattern, but a different code structure and no decodable address 42 Not added
Not code: a scanner, an indicator list, a test or an audit note 21 Not added

One repository in the first 231 matched only through a security audit note, not code. I removed the loader label from it; its separate PolinRider proof stays. That gives 231 − 1 + 384 = 614.

On 31 October 33 were added and one removed. Nine were found by the daily tracker and also carry PolinRider. 24 are among the 42 not added above: their current .env gives no decodable address, but an .env committed earlier in their history decodes to an address already in the proven set. One repository in the 614 matched only in a coding-assistant notes file, with no loader in any code file, so I removed it. That gives 614 + 33 − 1 = 646.

On 1 November I added three more, found by the daily tracker on the evening of 31 October. Each loader file was pushed on 31 October and was read again at a pinned commit on 1 November. Two of the three owners are new to the set. That gives 649.

Where it hides

#
Full data table · 9 rows
File Repositories
index.ts 113
server.ts 89
vite.config.ts 82
main.ts 47
app.ts 25
next.config.ts 7
nuxt.config.ts 6
prisma.config.ts 7
Other 163

The table covers the 539 repositories listed under their loader file. The other 110 also carry PolinRider and are listed under that proof. 531 of the 539 files are TypeScript. Build configurations matter most here: vite.config.ts or next.config.ts runs on every npm run dev, before any application code.

Who is affected

#
Measure Value
Proven repositories 649
Owner accounts 379
…also owning a proven PolinRider repository 193
Repositories owned by user accounts / organizations (609 read) 520 / 89
Forks 0
Not proven at the current commit (first set) 3 (no evidence), 1 deleted

Repository creation years (609 read): 302 in 2025 and 226 in 2026. The other 81 were created between 2014 and 2024. Last pushes cluster in October 2026 (195), August (162) and September (86).

Repositories with the AUTH_API_KEY loader, by creation year

Sample at a date: 609 of the 614 repositories in the 30 October 2026 set, platform creation dates read on 28–30 October. The 35 repositories added later are not included. Most were created in 2025–2026.

Table view
Repositories with the AUTH_API_KEY loader, by creation year
ItemValueNote
20141
20150
20160
20170
20184
20190
20203
20216
20229
202314
202444
2025302
2026226
Owners of AUTH_API_KEY repositories that also carry PolinRider

379 owner accounts after the 1 November 2026 additions, compared by owner id with the proven PolinRider owners.

Table view
Owners of AUTH_API_KEY repositories that also carry PolinRider
ItemValueNote
Also own a PolinRider repo193
AUTH_API_KEY only186

The addresses

#
Full data table · 10 rows
Address (defanged) Repositories pointing to it
auth-confirm-eight[.]vercel[.]app 229
files[.]catbox[.]moe (various file names) 182
auth-den-tdu[.]vercel[.]app 26
fortesting-alpha[.]vercel[.]app 21
auth-confirm-two[.]vercel[.]app 21
auth-confirm-seven[.]vercel[.]app 16
auth-rho-dun[.]vercel[.]app 13
auth-con-firm[.]vercel[.]app 10
data-kappa[.]vercel[.]app 9
22 more Vercel apps 1–7 each

Each repository points to one address. Catbox and Vercel are public hosting services and are abused here; neither is attacker-owned.

How it relates to the other activities

#
  • PolinRider. 193 of the 379 owners overlap. Both loaders appear in the same developer accounts, and both evaluate code fetched at run time. The data shows shared victims. It does not show whether one operator runs both, or whether a machine infected by one later received the other.
  • Contagious Interview. Its errorHandler loader also evaluates a remote response, and the public 2025 description of this pattern came from a fake-recruitment project. I count the two separately, because the code and the hosting differ.

No attribution is made here.

Evidence register

#
Expand technical detail · complete record
E-AA-PROOFPinned-commit proof rows

649 repositories with repository id, owner id, commit, path, git object id and the matched rule (atob on an environment variable plus eval or Function).

Source: GitHub GraphQL, read from an isolated virtual machine · Collected 30 October 2026

E-AA-REVIEWReview of the paged search

447 repository files found by the paged search on 30 October, each compared with the proven files by git object id, normalised code structure and decoded .env host; tier rules fixed before counting; 384 added, 63 not added.

Source: GitHub GraphQL and code search, read from an isolated virtual machine · Collected 30 October 2026

E-AA-ENVDecoded .env values

609 of the 614 repositories read on 28–30 October; 583 commit an .env file, and in 571 the AUTH_API_KEY value decodes to a URL. Only host names and file paths were kept.

Source: GitHub GraphQL, decoded in an isolated virtual machine · Collected 30 October 2026

E-AA-OVERLAPOwner overlap with PolinRider

Owner ids of proven AUTH_API repositories compared with owner ids of proven PolinRider repositories in the same proof set.

Source: Proof-pass tables · Collected 30 October 2026

Data notes and reconciliation

#
Expand technical detail · complete record
Measure Figures Why they differ
Proven repositories 231 → 614 → 646 → 649 First unpaged search (one audit-note match later removed) → paged search, +384 (30 Oct) → +33 from the extended search, −1 notes-file match (31 Oct) → +3 from the daily tracker (1 Nov); frozen
Owner accounts 363 → 377 → 379 Same steps
Listed under the loader file vs also PolinRider 539 / 110 A repository carrying both loaders is listed under its PolinRider file; both are in the 649
Overlap with PolinRider owners 193 Unchanged by the 1 November additions

Defensive checks

#
  1. Search your code for atob(process.env next to eval(, new Function( or Function(.
  2. Never commit .env, and treat a base64 value that decodes to a URL as hostile.
  3. Read build configurations (vite.config.*, next.config.*, nuxt.config.*) before running a project you did not write. They execute on npm run dev.
  4. If you find it, also check for PolinRider. Look for .vscode/tasks.json with folderOpen, and for build configs with long whitespace runs.

Indicators

#
Expand technical detail · complete record
AUTH_API_KEY loader (hosting services abused; not attacker-owned domains)
0 selected · 7 of 7 shown
TypeValueContextLookupsCopy
domainauth-confirm-eight[.]vercel[.]appDecoded from AUTH_API_KEY in 229 repositories
Confidence: Confirmed malicious
domainauth-den-tdu[.]vercel[.]appDecoded from AUTH_API_KEY in 26 repositories
Confidence: Confirmed malicious
domainfortesting-alpha[.]vercel[.]appDecoded from AUTH_API_KEY in 21 repositories
Confidence: Confirmed malicious
domainauth-confirm-two[.]vercel[.]appDecoded from AUTH_API_KEY in 21 repositories
Confidence: Confirmed malicious
domainauth-confirm-seven[.]vercel[.]appDecoded from AUTH_API_KEY in 16 repositories
Confidence: Confirmed malicious
domainauth-rho-dun[.]vercel[.]appDecoded from AUTH_API_KEY in 13 repositories
Confidence: Confirmed malicious
domainfiles[.]catbox[.]moePublic file host; payload paths decoded from AUTH_API_KEY in 182 repositories
Confidence: High-confidence suspicious

    What was already public

    #
    Expand technical detail · complete record

    Sources were read on or before 30 October 2026. Each one also links to its Internet Archive record, which lists the captures that exist, in case the original moves or changes.

    1. happihacking, "Exposing the 'Multibank Crypto Poker' Recruitment Scam" (20 August 2025): a fake-recruitment project whose routes/api/auth.js decodes AUTH_API_KEY with atob, fetches the decoded URL and passes the response to eval. Its payload host is a different Vercel app from those listed here. https://happihacking.com/blog/posts/2025/recruitment_scam/ (Internet Archive lookup)

    Not found in the public sources I checked, as of 30 October 2026: a count of affected repositories, the Vercel app names above, and the overlap with PolinRider victims.

    Limitations

    #
    • The payloads were not downloaded, so what they do is not known from this work.
    • Five repositories were not read for .env and are missing from the address and year tables.
    • The proof rule requires atob on an environment variable plus eval or Function. A file that loads the code some other way would be missed.
    • 42 repositories with the same pattern but no decodable address are not counted.
    • Code search covers default branches only, so this is a floor.

    Investigation context

    #

    This article is part of the GitHub threat investigation. Counts are a snapshot, not evidence that the code ran on a victim's machine. The shared methodology and proof rules explain inclusion, exclusions and the independent re-check.

    Claims, evidence and limits

    ClaimAssessmentEvidenceLimit
    649 repositories carry the loader at a pinned commitConfidence: Confirmed malicious
    E-AA-PROOF, E-AA-REVIEWNot stated in the finding
    The payload address is kept in .envConfidence: Confirmed malicious
    E-AA-ENVNot stated in the finding
    Many victims are also PolinRider victimsConfidence: High-confidence suspicious
    E-AA-PROOF, E-AA-OVERLAPNot stated in the finding

    Terms used on this page

    pinned commit
    A specific commit, identified by its hash, at which a repository was read. Every counted repository was proven at the default-branch commit recorded in the proof pass, so a later change to the repository does not change what was proven.
    default branch
    The branch a repository shows by default. Counts are taken on default branches only; infected non-default branches were recorded separately and are not counted.
    proof pass
    The 30–31 October 2026 re-read of every candidate at a pinned commit under fixed inclusion rules, followed by an independent re-read of a random sample of 555 repositories through a different API.
    defanged
    Written so it cannot be clicked or resolved as written, for example example[.]com or hxxps://. Every network indicator on this site is defanged.

    All terms: glossary.

    Corrections & discussion

    Found an error, or have evidence that supports or contradicts a finding? Send a correction. Name the section and what you observed.

    ← All research