// threat brief
PostHog's official npm packages hit by the Shai-Hulud 2.0 worm: a misconfigured GitHub workflow handed attackers the publish token
- CVSS
- —
- CISA KEV
- Not listed
- Weakness
- CWE-506
- Topics
- Supply chain
AI review 2026-09-30 (not human-reviewed)Command tests: none yetReproduced: not yetVerification record ↓
Attackers stole PostHog's npm publish token through a misconfigured GitHub workflow and shipped worm-infected SDKs that steal credentials on install.
| Who is affected | Developer machines or CI jobs that installed posthog-node 4.18.1/5.11.3/5.13.3, posthog-js 1.297.3 or another malicious PostHog version (OSV lists at least 56 packages) from 04:04 UTC on 2025-11-24 until the versions were deleted (PostHog says 09:30 UTC; some packages show later last-modified times on npm), with install scripts allowed to run. PlainCVE's judgement: machines that later reinstalled one of those versions from a lockfile, local cache or internal mirror should also be treated as affected (PostHog advises clearing caches, but no source addresses this case directly). |
|---|---|
| What happens | The malware runs at install time; the package never has to be imported. PostHog says it searches for credentials of any kind with TruffleHog, posts them to new public GitHub repositories, and uses stolen npm tokens to infect other packages. According to Datadog, StepSecurity and Wiz, it also collects AWS/GCP/Azure cloud credentials and leaves a remote-control backdoor; according to Aikido and Datadog, it wipes the home directory if it can neither steal credentials nor spread. |
| What to do now | Check lockfiles for the malicious versions. If you find one, treat the machine as fully compromised: rotate every credential from a separate clean computer, delete node_modules and the package cache, and move to PostHog's named safe versions (e.g. posthog-node 5.14.0, posthog-js 1.298.0). |
| Affected versions | OSV lists malicious versions of 56 PostHog packages (full list under Who is affected), mainly posthog-node 4.18.1, 5.11.3, 5.13.3; posthog-js 1.297.3; posthog-react-native 4.11.1, 4.12.5; posthog-docusaurus 2.0.6; posthog-react-native-session-replay 1.2.2; @posthog/core 1.5.6; @posthog/agent 1.24.1; @posthog/ai 7.1.2; @posthog/cli 0.5.15; @posthog/wizard 1.18.1; @posthog/nuxt 1.2.9; posthog-plugin-hello-world 1.0.1 |
| Fixed versions | Safe versions named by PostHog: posthog-js 1.298.0, posthog-node 5.14.0, posthog-react-native 4.13.0, posthog-react-native-session-replay 1.2.3, @posthog/agent 1.24.2, @posthog/ai 7.2.0, @posthog/cli 0.5.16, @posthog/wizard 1.18.2, posthog-plugin-hello-world 1.0.0. No safe version has been named individually for the other packages (including posthog-docusaurus and @posthog/core); PostHog's post-mortem says the latest versions published after it discovered the attack are known-good. |
| Timeline |
|
Blue team playbook
Check, fix / mitigate, respond, harden. Matching the affected versions does not mean you were compromised, and a check that finds nothing does not prove you are safe; each item says what it can and cannot show. Tagged steps point to where the attack flow below can be stopped.
- 01Check whether a project installed a malicious version
List every package in the project's dependency tree whose name contains posthog, with its version, and compare against the table and the OSV list under Who is affected. Check lockfiles too, because later reinstalls follow them.
Read-only checknpm ls --all 2>/dev/null | grep -i posthog - 02Search for file names published in the reports
File names listed by PostHog and Wiz: setup_bun.js, bun_environment.js, cloud.json, contents.json, environment.json, truffleSecrets.json. Also look for an unexpected new ~/.bun/bin/bun.
Read-only checkfind . ~ \( -name setup_bun.js -o -name bun_environment.js -o -name truffleSecrets.json -o -name cloud.json -o -name contents.json -o -name environment.json \) 2>/dev/null - 03Search npm logs
PostHog suggests looking in npm logs for entries containing "shai" or "preinstall".
Read-only checkgrep -rniE 'shai|preinstall' ~/.npm/_logs/ - 04Check GitHub accounts and orgs for backdoor traces
According to StepSecurity and Datadog, the .github/workflows/discussion.yaml backdoor workflow and the self-hosted runner named SHA1HULUD are attached to new repositories the worm creates with your token, not to your existing projects, so the thing to check is the GitHub account itself. List the account's repositories (requires an authenticated GitHub CLI; an org name works too) and look for recent public repositories you don't recognise: Datadog says their names are 18 random lowercase letters and digits and their description is "Sha1-Hulud: The Second Coming."
Read-only checkgh repo list YOUR_ACCOUNT --limit 100 --json name,description,createdAt,visibility
- 01Move to PostHog's named safe versions and pin them
For example posthog-js 1.298.0 and posthog-node 5.14.0. PostHog's post-mortem says the latest versions, published after it discovered the attack, are known-good.
Read-only checknpm view posthog-node versions --json - 02Delete node_modules and clear the package cache
GitHub's advisory for @posthog/core advises removing the package and rotating all secrets from a different computer; PostHog's issue advises deleting node_modules and running pnpm cache delete if you use pnpm; PostHog's post-mortem and GitLab's advisory for posthog-js also advise clearing the npm cache. PlainCVE's view is that this also keeps a malicious version from being reinstalled later from cache or an old lockfile.
- 03Turn off install scripts in CI
The malware starts from a preinstall script; if install scripts don't run, it isn't triggered.
Read-only checknpm config get ignore-scripts
- 01Treat the machine as fully compromised and rotate credentials from a clean computer
Rotate every secret the machine could reach: GitHub PATs, npm tokens, SSH keys, cloud keys and CI secrets. Removing the package alone is not enough; the attacker may already have full access.
- 02Remove attacker-created repositories, runners and workflows
Delete repositories the attacker created, remove rogue self-hosted runners and workflows, and review GitHub security logs (StepSecurity advice).
- 03Check whether packages you maintain were republished
The worm uses stolen npm tokens to infect packages the victim maintains, so maintainers should check their packages for unexpected recent versions.
Read-only checknpm view YOUR_PACKAGE time --json
- 01Never run PR code under pull_request_target
Use pull_request, or split into an unprivileged pull_request job plus a privileged workflow_run job; don't rely on label gates. Require security review of workflow changes.
Read-only checkgrep -rn 'pull_request_target' .github/workflows/ - 02Move to trusted publishing
With no long-lived npm token stored in CI, a leak does far less damage. PostHog moved to this after the incident.
- 03Set a cooldown for new versions
PostHog recommends a minimumReleaseAge of 3 days or more; StepSecurity suggests 2 days. This time, according to PostHog, the malicious versions were deleted after about 5 hours.
- 04Use phishing-resistant MFA
Recommended by Wiz; GitHub's npm plan also requires 2FA for local publishing, with FIDO keys replacing one-time codes.
Full remediation steps and notes
- Check whether you installed it. Compare lockfiles and
node_moduleswith the table and the list below it (including reinstalls from a lockfile or cache after the versions were deleted). Search forsetup_bun.js,bun_environment.js,cloud.json,contents.json,environment.jsonandtruffleSecrets.json. Look in npm logs for “shai” or “preinstall”. Also check your GitHub accounts and orgs for unexpected new repositories you don’t recognise: according to StepSecurity and Datadog, theSHA1HULUDrunner and thediscussion.yamlbackdoor workflow are attached to these worm-created repositories. - If you did, treat the machine as fully compromised. From a separate, clean computer, rotate every secret the machine could reach: GitHub PATs, npm tokens, SSH keys, cloud keys and CI secrets. Removing the package alone is not enough, because the attacker may already have full access.
- Clean up. Remove the package, delete
node_modules, and clear the npm or pnpm cache. Delete attacker-created repositories, remove rogue runners and workflows, and review GitHub security logs. If you maintain npm packages, check them for unexpected new versions. - Move to safe versions and pin them. For example posthog-node 5.14.0 and posthog-js 1.298.0 (full list in the table). The GitHub and GitLab advisories say “no patched version”, which is how those databases usually record malware; PostHog itself has published safe versions.
- Harden for the long term.
- Add a cooldown for new versions: PostHog recommends a
minimumReleaseAgeof 3 days or more; StepSecurity suggests 2 days. - Turn off or restrict install scripts in CI.
- Use phishing-resistant MFA.
- If you maintain packages: move to trusted publishing, never run PR code under
pull_request_target, and require security review of workflow changes.
- Add a cooldown for new versions: PostHog recommends a
PostHog had already hired a dedicated security engineer in July 2025, before the incident, and says its November incident response and post-mortem relied on the tools and practices put in place since then. After the incident, PostHog moved to trusted publishing, put workflow changes under security-team review, upgraded to pnpm 10, and redesigned how it manages GitHub secrets.
Attack flow
Read left to right: this is the order the attack happens in. A blue shield means the step can be stopped; the earlier, the better. Click an icon for details.
Read top to bottom: this is the order the attack happens in. A blue shield means the step can be stopped; the earlier, the better. Tap an icon for details.
- Attacker
The attacker opens a malicious pull request against PostHog's repository
A workflow PostHog merged on 2025-09-11 used the pull_request_target trigger but also ran code taken from the PR itself (a reviewer-assignment script).
Defense: Never run outside PR code under pull_request_target; use plain pull_request for untrusted code.
- Your system
The attacker's code runs in the trusted CI context and steals a bot account's GitHub token
pull_request_target carries the base repository's secrets and write access, so code from the PR gets the same privileges. GitHub Security Lab calls this pattern a "pwn request".
Defense: Split the job: an unprivileged pull_request job, then a privileged workflow_run job that only handles its results; give bot tokens least privilege.
- Attacker
The attacker uses the stolen token to change a workflow and dump every secret, including the npm publish token
On 2025-11-23 the attacker tested the token, then used a detached commit to change the Lint PR workflow so it sent out all GitHub secrets.
Defense: Require security review of workflow changes; move to trusted publishing so no long-lived npm token sits in CI.
- Attacker
The attacker publishes worm-infected versions to npm
PostHog's post-mortem says they were published at 04:11 UTC on 2025-11-24; npm registry metadata shows posthog-node 5.11.3 at 04:04, 5.13.3 at 04:09 and 4.18.1 at 04:13. PostHog says that by 09:30 it had found and deleted the malicious packages and revoked the tokens.
Defense: Use a package cooldown (e.g. pnpm's minimumReleaseAge) so new versions are adopted only after a few days.
- Your system
On install, a preinstall script runs the malware automatically
According to PostHog, it searches for credentials of any kind with TruffleHog and creates public repositories with the victim's token to post the data. According to Datadog and StepSecurity, it first installs the Bun runtime and then runs an obfuscated payload, also collects AWS, GCP and Azure cloud credentials, and registers a self-hosted runner named SHA1HULUD as a backdoor.
Defense: Disable or restrict install scripts in CI (for example, pnpm 10 does not run dependency install scripts by default).
- External
The worm uses stolen npm tokens to infect other packages the victim maintains
According to Aikido, up to 100 packages per victim (the first wave's limit was 20). According to Aikido and Datadog, if it cannot steal credentials or spread, it tries to wipe the home directory.
Defense: Rotate every token from a separate clean computer; maintainers should move to trusted publishing and phishing-resistant MFA.
This is not an ordinary software bug but a malicious package incident: it has no CVE ID and is not in NVD or the CISA KEV catalog. This page uses the OSV malicious-package ID MAL-2025-190925.
Who is affected
The malware runs at install time. Your code never has to import or call these packages. Treat yourself as affected if a developer machine or CI job ran npm install (or similar), pulled in one of the versions in the table or the list below it, and allowed install scripts to run. The most likely window is from 04:04 UTC on 2025-11-24 until the versions were deleted (PostHog says by 09:30 UTC). PlainCVE’s judgement is that if a lockfile already recorded a malicious version, or a local cache or internal package mirror kept a copy, a later reinstall could still bring it back, so check those too. No source addresses this case directly, but PostHog’s post-mortem and issue both advise deleting cached dependencies.
| Package | Malicious versions | Safe version named by PostHog | OSV ID / notes |
|---|---|---|---|
| posthog-node | 4.18.1, 5.11.3, 5.13.3 | 5.14.0 | MAL-2025-190925 (main ID for this page) |
| posthog-js | 1.297.3 | 1.298.0 | MAL-2025-191402; GitLab GMS-2025-554 |
| posthog-react-native | 4.11.1, 4.12.5 | 4.13.0 | MAL-2025-190926; PostHog lists only 4.11.1; 4.12.5 appears in OSV and npm registry metadata (published 04:09 UTC, since deleted) |
| posthog-react-native-session-replay | 1.2.2 | 1.2.3 | MAL-2025-190649 |
| posthog-docusaurus | 2.0.6 | Not named | MAL-2025-190924 |
| @posthog/agent | 1.24.1 | 1.24.2 | MAL-2025-190748 |
| @posthog/ai | 7.1.2 | 7.2.0 | MAL-2025-190644 |
| @posthog/cli | 0.5.15 | 0.5.16 | MAL-2025-190671 |
| @posthog/wizard | 1.18.1 | 1.18.2 | MAL-2025-190900; listed in PostHog’s issue but not the post-mortem |
| @posthog/core | 1.5.6 | Not named | MAL-2025-190645, GHSA-3f6v-43gc-g8fr; PostHog does not list it |
| posthog-plugin-hello-world | 1.0.1 | 1.0.0 | MAL-2025-190778, GHSA-9mwj-fr6x-j3hg; PostHog’s issue lists only 1.0.0, as a safe version |
PostHog’s post-mortem lists only the “most relevant” packages. OSV also lists the following PostHog packages as malicious (compiled from this page’s OSV query on 2026-09-28; each package has its own MAL ID, searchable by package name on osv.dev; PostHog has not named individual safe versions for them):
- @posthog/clickhouse 1.7.1, @posthog/hedgehog-mode 0.0.42, @posthog/icons 0.36.1, @posthog/lemon-ui 0.0.1, @posthog/nextjs-config 1.5.1, @posthog/nuxt 1.2.9, @posthog/piscina 3.2.1, @posthog/plugin-contrib 0.0.6, @posthog/plugin-server 1.10.8, @posthog/rrweb 0.0.31, @posthog/rrweb-player 0.0.31, @posthog/rrweb-record 0.0.31, @posthog/rrweb-replay 0.0.19, @posthog/siphash 1.1.2, @posthog/web-dev-server 1.0.5
- Version 0.0.8 of these 30 plugin packages: @posthog/automatic-cohorts-plugin, @posthog/bitbucket-release-tracker, @posthog/currency-normalization-plugin, @posthog/customerio-plugin, @posthog/databricks-plugin, @posthog/drop-events-on-property-plugin, @posthog/event-sequence-timer-plugin, @posthog/filter-out-plugin, @posthog/first-time-event-tracker, @posthog/geoip-plugin, @posthog/github-release-tracking-plugin, @posthog/gitub-star-sync-plugin, @posthog/heartbeat-plugin, @posthog/ingestion-alert-plugin, @posthog/intercom-plugin, @posthog/kinesis-plugin, @posthog/laudspeaker-plugin, @posthog/migrator3000-plugin, @posthog/netdata-event-processing, @posthog/pagerduty-plugin, @posthog/plugin-unduplicates, @posthog/postgres-plugin, @posthog/sendgrid-plugin, @posthog/snowflake-export-plugin, @posthog/taxonomy-plugin, @posthog/twilio-plugin, @posthog/twitter-followers-plugin, @posthog/url-normalizer-plugin, @posthog/variance-plugin, @posthog/zendesk-plugin
npm registry metadata shows every malicious version of these 56 packages was published between 04:04 and 04:13 UTC on 2025-11-24, and none of them is in npm’s version lists any more. This page’s query covered npm packages whose names contain posthog, so it is not guaranteed to be complete.
Other points:
- PostHog’s post-mortem says the latest versions, published after it discovered the attack, are known-good. The malicious versions were deleted from npm and replaced with newer releases; see the table for the safe versions.
- Deletion time: PostHog says it had found and deleted the malicious packages by 09:30 UTC. However, npm registry metadata shows posthog-docusaurus last modified at 09:57 UTC and posthog-plugin-hello-world at 14:11 UTC. The registry does not record deletion times directly, so it is not certain every package was gone by 09:30.
- Publish times: PostHog’s post-mortem says 04:11 UTC, while npm registry metadata shows posthog-node 5.11.3 published at 04:04 UTC, 5.13.3 at 04:09 UTC and 4.18.1 at 04:13 UTC. This page uses 04:04 as the start of the exposure window.
- PostHog says it knows of no customer production deployments that used the malicious versions.
- Installs that skip lifecycle scripts were less exposed (for example, pnpm 10 does not run dependency install scripts by default).
- Wiz found posthog-node in 25% and posthog-js in 15% of the environments it scanned. That shows how common the packages are, not how many were infected.
How it works
There are two layers here: how PostHog itself was breached, and what the malicious package did to whoever installed it.
Layer one: a “pwn request” handed over the publish token
GitHub Actions’ pull_request_target trigger runs as the base repository, with its secrets and write access. It exists so the workflow definition comes from the trusted main branch. The trouble starts when the workflow then checks out and runs code from an outside pull request: that stranger’s code gets the same privileges. GitHub Security Lab calls this mistake a “pwn request”.
A workflow PostHog merged on 2025-09-11 did exactly that. It used pull_request_target and also ran a reviewer-assignment script taken from the PR. In its post-mortem PostHog admits its developers believed pull_request_target protected the code being run, when in fact it only controls where the workflow definition is read from.
What happened (conceptual summary):
- The attacker opened a malicious PR, and its code ran in the trusted CI context.
- That code stole a bot account’s GitHub personal access token (PAT).
- The attacker used the token to change a workflow and dump the repository’s secrets, including the npm publish token.
- The attacker used the npm token to publish worm-infected versions of the packages.
The flawed pattern (conceptual illustration, not PostHog’s actual configuration):
Show code example(yaml)
on: pull_request_target # runs with base-repo permissions and secrets
jobs:
assign:
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.sha }} # checks out outside PR code
- run: node scripts/assign-reviewers.js # runs the PR's code → it gets the secretsA safer pattern:
Show code example(yaml)
on: pull_request # outside PRs run with no secrets and read-only access
permissions:
contents: read
jobs:
check:
steps:
- uses: actions/checkout@v4
- run: node scripts/check.js
# Anything that needs write access goes in a separate workflow triggered by workflow_run,
# which only handles the results and never runs PR codeLayer two: what the Shai-Hulud 2.0 worm did
This summary comes from the research reports. This page did not open any package file or malware sample.
- Starts at install: a
preinstallscript (setup_bun.js) installs the Bun JavaScript runtime, then runs a large obfuscated program,bun_environment.js. Datadog notes that using Bun helps it avoid tools that only watch Node.js. - Steals credentials: PostHog says it uses the open-source TruffleHog scanner to search the environment for credentials of any kind. According to Datadog and StepSecurity, it also collects GitHub tokens, npm tokens, environment variables, and AWS, GCP and Azure cloud credentials and secret-manager contents; Wiz counted hundreds of cloud credentials among those leaked across the whole wave.
- Posts them publicly: it creates new public GitHub repositories with the victim’s own token and posts the stolen data there. Wiz also saw one victim’s secrets posted under unrelated users’ accounts.
- Leaves a backdoor: it registers a self-hosted GitHub Actions runner named
SHA1HULUDand adds.github/workflows/discussion.yaml. That workflow listens for new GitHub Discussions, so the attacker can run commands on the machine registered as that runner just by creating a new Discussion in the victim’s repository with the command in its body. - Spreads itself: with a stolen npm token it infects up to 100 packages the victim maintains (according to Aikido, the first wave’s limit was 20).
- Destroys data: according to Aikido and Datadog, if it can neither steal credentials nor spread, it tries to wipe the user’s home directory. StepSecurity also describes Linux file overwriting meant to erase evidence.
- Other behaviour: it runs immediately in CI but in the background on developer machines, and it tries to escape Docker containers through privileged mounts.
This maps to CWE-506 (Embedded Malicious Code).
Timeline
| When (UTC) | Event |
|---|---|
| 2025-09-11 | PostHog merges the vulnerable workflow change |
| 2025-09-14 or 09-16 | First Shai-Hulud wave (GitHub dates it the 14th, Aikido the 16th) |
| 2025-11-18 5:40 PM (the post-mortem gives no time zone; its screenshots are in UTC+2, so this may not be UTC) | Attacker account opens the malicious PR; bot PAT stolen |
| 2025-11-23 15:28 | Attacker deletes a workflow run to test the token |
| 2025-11-23 15:43–15:45 | Attacker uses a detached commit to change the Lint PR workflow; all GitHub secrets, including the npm token, are sent out |
| 2025-11-24 ~03:16 | Second wave, “Sha1-Hulud: The Second Coming”, begins, with AsyncAPI hit first (Aikido) |
| 2025-11-24 04:04–04:13 | Malicious PostHog versions published to npm (npm registry: posthog-node 5.11.3 at 04:04, 5.13.3 at 04:09, 4.18.1 at 04:13, most other PostHog packages at 04:08–04:10; PostHog’s post-mortem says 04:11) |
| 2025-11-24 09:30 | PostHog says it had found and deleted the malicious packages and revoked tokens by this time |
| 2025-11-24 09:57, 14:11 | npm registry last-modified times for posthog-docusaurus and posthog-plugin-hello-world (the registry does not record deletion times directly) |
| 2025-11-24 | PostHog opens GitHub issue #2633; GitHub publishes the @posthog/core advisory; OSV publishes the posthog-node record MAL-2025-190925 at 16:31 |
| 2025-11-25 | OSV publishes the posthog-js record; GitLab publishes GMS-2025-554 |
| 2025-11-26 | PostHog publishes its post-mortem |
| CVE / KEV | None; not applicable |
Further reading
- Scoring: there is no CVE and no NVD score. GitHub’s advisory for @posthog/core gives no CVSS number and rates it Critical (Malware). GitLab gives posthog-js a CVSS 3.1 score of 8.6 (
AV:L/AC:L/PR:N/UI:R/S:C/C:H/I:H/A:H). Because that score is for a different package, this page leaves its severity field empty. - Where sources disagree:
- Scope of affected packages: PostHog’s post-mortem lists only the 8 “most relevant” packages (10 versions) and its issue adds @posthog/wizard, for 9 in total; OSV lists 56 PostHog packages, including @posthog/core, posthog-react-native 4.12.5 and posthog-plugin-hello-world.
- Deletion time: PostHog says by 09:30 UTC; npm registry shows last-modified times of 09:57 UTC for posthog-docusaurus and 14:11 UTC for posthog-plugin-hello-world.
- Publish time of the malicious versions: PostHog’s post-mortem says 04:11 UTC, but npm registry metadata shows posthog-node 5.11.3 was already published at 04:04 UTC.
- Size of the whole wave: Aikido counts 492 packages; Datadog counts 796 and calls that a lower bound; Wiz estimates about 700 to 800, and also counted 775 exposed GitHub tokens plus 373 AWS, 300 GCP and 115 Azure credentials; StepSecurity’s early count was more than 70; PostHog’s issue says “over 300”.
- Repository description strings: StepSecurity and Aikido report “Sha1-Hulud: The Second Coming”. Wiz separately reports a potential second phase first seen at 22:45 UTC on 11/25, in which one victim’s private repositories were made public using credentials stolen in phase one; the description used for that phase is “Sha1-Hulud: The Continued Coming”.
- Wiz says the second wave reuses the first wave’s name and methods but may be run by different people.
- Where PostHog fits in: PostHog was breached through its own CI misconfiguration, not because a maintainer installed an infected package. Datadog calls this one of several ways the attackers got in. Once the malicious versions were published, PostHog’s packages became carriers that could infect other maintainers.
- npm context: the attack came shortly before npm’s 2025-12-09 deadline for revoking classic tokens. GitHub’s September 2025 plan includes retiring classic tokens, granular tokens that last at most 7 days, trusted publishing, and required 2FA for local publishing with FIDO keys replacing one-time codes.
- AI angle: two affected packages are named
@posthog/aiand@posthog/agent, but no source links the attack itself to AI, and this page does not suggest one. - Could not be confirmed: whether GitHub has GHSA advisories for posthog-node and posthog-js; how many times the malicious versions were downloaded while live; what CISA’s alert on the first Shai-Hulud wave says (the page could not be read and is not used here).
Verification Checked item by item against sources by AI under our review rules, all passed; not human-reviewed and not yet reproduced in our lab
| AI review | 2026-09-30 · review rules |
|---|
Verification records describe the environment and the result only, never reproduction steps or code that could attack other people’s systems. See our policy.
Sources
- VendorShai-Hulud 2.0 Supply Chain Attack Post-Mortem · PostHog, 2025-11-26 · accessed 2026-09-28
- Otherposthog-node package metadata (npm registry) · npm · accessed 2026-09-28time field: 5.11.3 published 2025-11-24T04:04:24Z, 5.13.3 at 04:09:08Z, 4.18.1 at 04:13:22Z
- Advisoryposthog-js and other packages were temporarily compromised - November 24th - SHA1 HULUD (issue #2633) · PostHog (GitHub), 2025-11-24 · accessed 2026-09-28
- AdvisoryMAL-2025-190925: Malicious code in posthog-node (npm) · OSV, 2025-11-24 · accessed 2026-09-23
- AdvisoryMAL-2025-190926: Malicious code in posthog-react-native (npm) · OSV, 2025-11-24 · accessed 2026-09-28Malicious versions 4.12.5 and 4.11.1
- AdvisoryMAL-2025-190900: Malicious code in @posthog/wizard (npm) · OSV, 2025-11-24 · accessed 2026-09-28
- AdvisoryMAL-2025-190924: Malicious code in posthog-docusaurus (npm) · OSV, 2025-11-24 · accessed 2026-09-28
- AdvisoryMAL-2025-190778: Malicious code in posthog-plugin-hello-world (npm) · OSV, 2025-11-24 · accessed 2026-09-28
- AdvisoryMalware in posthog-plugin-hello-world (GHSA-9mwj-fr6x-j3hg) · GitHub Advisory Database, 2025-11-24 · accessed 2026-09-28
- AdvisoryMAL-2025-190753: Malicious code in @posthog/siphash (npm) · OSV, 2025-11-24 · accessed 2026-09-28
- AdvisoryMAL-2025-191292: Malicious code in @posthog/icons (npm) · OSV, 2025-11-25 · accessed 2026-09-28
- AdvisoryMAL-2025-190887: Malicious code in @posthog/nextjs-config (npm) · OSV, 2025-11-24 · accessed 2026-09-28
- AdvisoryMAL-2025-190749: Malicious code in @posthog/nuxt (npm) · OSV, 2025-11-24 · accessed 2026-09-28
- AdvisoryOSV search results for npm packages matching posthog · OSV · accessed 2026-09-28This page queried the OSV API on 2026-09-28 for each npm package whose name contains posthog and found MAL records for 56 PostHog packages
- Otherposthog-docusaurus package metadata (npm registry) · npm · accessed 2026-09-28time field: 2.0.6 published 2025-11-24T04:08:49Z; modified 2025-11-24T09:57:58Z; 2.0.6 is no longer in the version list
- Otherposthog-react-native package metadata (npm registry) · npm · accessed 2026-09-28time field: 4.12.5 published 2025-11-24T04:09:10Z, 4.11.1 at 04:10:52Z; neither is in the version list any more
- Otherposthog-plugin-hello-world package metadata (npm registry) · npm · accessed 2026-09-28time field: 1.0.1 published 2025-11-24T04:08:55Z; modified 2025-11-24T14:11:50Z; 1.0.1 is no longer in the version list
- AdvisoryMAL-2025-191402: Malicious code in posthog-js (npm) · OSV, 2025-11-25 · accessed 2026-09-23
- AdvisoryMAL-2025-190645: Malicious code in @posthog/core (npm) · OSV, 2025-11-24 · accessed 2026-09-23
- AdvisoryMalware in @posthog/core (GHSA-3f6v-43gc-g8fr) · GitHub Advisory Database, 2025-11-24 · accessed 2026-09-28
- AdvisoryAdvisories for Npm/Posthog-Js package · GitLab Advisory Database · accessed 2026-09-23
- Advisoryposthog-js contains malware after npm account takeover (GMS-2025-554) · GitLab Advisory Database, 2025-11-25 · accessed 2026-09-28
- ResearchShai-Hulud 2.0: Ongoing Supply Chain Attack · Wiz, 2025-11-24 · accessed 2026-09-28
- ResearchSha1-Hulud: The Second Coming – Zapier, ENS Domains, and Other Prominent NPM Packages Compromised · StepSecurity, 2025-11-23 · accessed 2026-09-28
- ResearchShai-Hulud 2.0 npm worm analysis · Datadog Security Labs, 2025-11-25 · accessed 2026-09-28
- ResearchShai Hulud strikes again, hitting Zapier & ENSDomains · Aikido Security, 2025-11-24 · accessed 2026-09-23
- ResearchKeeping your GitHub Actions and workflows secure Part 1: Preventing pwn requests · GitHub Security Lab, 2021-08-03 · accessed 2026-09-23
- VendorOur plan for a more secure npm supply chain · GitHub Blog, 2025-09-22 · accessed 2026-09-23
Press brief
In one sentence
On November 24, 2025, the official JavaScript packages of analytics company PostHog were replaced on npm with malicious versions carrying a self-spreading worm called "Sha1-Hulud: The Second Coming" (Shai-Hulud 2.0). Attackers first abused a misconfigured GitHub automation workflow at PostHog to steal its publish token, then released versions that steal developer credentials at install time. PostHog says the malicious versions were deleted about five hours later and that it knows of no customer production deployments that used them. It was part of a wider wave that hit hundreds of npm packages.
Key facts
- Malicious versions published
- 2025-11-24 04:11 UTC (PostHog post-mortem); npm registry metadata shows posthog-node 5.11.3 at 04:04 UTC and 5.13.3 at 04:09 UTC; PostHog says they were deleted by 09:30 UTC posthog.com
- PostHog packages OSV lists as malicious
- 56 (this page's OSV query on 2026-09-28; may be incomplete) osv.dev
- Incident ID for this page
- MAL-2025-190925 (posthog-node 4.18.1, 5.11.3, 5.13.3) osv.dev
- Safe versions
- posthog-node 5.14.0, posthog-js 1.298.0 and others github.com
- Classification
- GitHub's advisory for @posthog/core (GHSA-3f6v-43gc-g8fr) lists CWE-506 Embedded Malicious Code and rates it Critical (Malware); GitLab's GMS-2025-554 for posthog-js also lists CWE-506 github.com
- Score
- No CVE or NVD score; GitLab gives posthog-js CVSS 3.1 8.6 advisories.gitlab.com
Confirmed
- Several official PostHog npm packages were published on 2025-11-24 in versions containing the Shai-Hulud 2.0 worm.
- The breach began with a PostHog GitHub Actions workflow that used pull_request_target while running code from the PR.
- The malware runs at install time through a preinstall script, steals credentials and tries to infect more packages.
- PostHog says it knows of no customer production deployments that used the malicious versions.
Unconfirmed / disputed
- PostHog has not published a complete list of affected packages. Its post-mortem lists only the 8 "most relevant" packages (10 versions) and its issue adds @posthog/wizard, for 9 in total, while OSV lists 56 PostHog packages as malicious (compiled from this page's 2026-09-28 query; may still be incomplete).
- Whether malicious versions could be reinstalled after deletion from a lockfile, local cache or internal mirror: PostHog and GitLab advise clearing caches, but no source addresses this directly; treating it as affected is PlainCVE's judgement.
- When each package was actually deleted from npm: PostHog says by 09:30 UTC, but npm registry shows posthog-docusaurus last modified at 09:57 UTC and posthog-plugin-hello-world at 14:11 UTC; the registry does not record the deletion time directly.
- Estimates of the whole wave differ: from about 490 to about 800 packages.
- No source gives how many times the malicious PostHog versions were downloaded while live.
- Most packages, including posthog-docusaurus and @posthog/core, have no individually named official safe version.
Quotable line
What was breached wasn't PostHog's product, but the automated pipeline that ships it.
Images
Download the share image (PNG) · Attack flow diagram (downloadable)
Please credit "PlainCVE" and link to this page. Full citation format is under "How to cite" below. Found a factual error? Report it.
PostHog's official npm packages hit by the Shai-Hulud 2.0 worm: a misconfigured GitHub workflow handed attackers the publish token On November 24, 2025, the official JavaScript packages of analytics company PostHog were replaced on npm with malicious versions carrying a self-spreading worm called "Sha1-Hulud: The Second Coming" (Shai-Hulud 2.0). Attackers first abused a misconfigured GitHub automation workflow at PostHog to steal its publish token, then released versions that steal developer credentials at install time. PostHog says the malicious versions were deleted about five hours later and that it knows of no customer production deployments that used them. It was part of a wider wave that hit hundreds of npm packages. Key facts: - Malicious versions published:2025-11-24 04:11 UTC (PostHog post-mortem); npm registry metadata shows posthog-node 5.11.3 at 04:04 UTC and 5.13.3 at 04:09 UTC; PostHog says they were deleted by 09:30 UTC(https://posthog.com/blog/nov-24-shai-hulud-attack-post-mortem) - PostHog packages OSV lists as malicious:56 (this page's OSV query on 2026-09-28; may be incomplete)(https://osv.dev/list?ecosystem=npm&q=posthog) - Incident ID for this page:MAL-2025-190925 (posthog-node 4.18.1, 5.11.3, 5.13.3)(https://osv.dev/vulnerability/MAL-2025-190925) - Safe versions:posthog-node 5.14.0, posthog-js 1.298.0 and others(https://github.com/PostHog/posthog-js/issues/2633) - Classification:GitHub's advisory for @posthog/core (GHSA-3f6v-43gc-g8fr) lists CWE-506 Embedded Malicious Code and rates it Critical (Malware); GitLab's GMS-2025-554 for posthog-js also lists CWE-506(https://github.com/advisories/GHSA-3f6v-43gc-g8fr) - Score:No CVE or NVD score; GitLab gives posthog-js CVSS 3.1 8.6(https://advisories.gitlab.com/npm/posthog-js/GMS-2025-554/) Confirmed: - Several official PostHog npm packages were published on 2025-11-24 in versions containing the Shai-Hulud 2.0 worm. - The breach began with a PostHog GitHub Actions workflow that used pull_request_target while running code from the PR. - The malware runs at install time through a preinstall script, steals credentials and tries to infect more packages. - PostHog says it knows of no customer production deployments that used the malicious versions. Unconfirmed / disputed: - PostHog has not published a complete list of affected packages. Its post-mortem lists only the 8 "most relevant" packages (10 versions) and its issue adds @posthog/wizard, for 9 in total, while OSV lists 56 PostHog packages as malicious (compiled from this page's 2026-09-28 query; may still be incomplete). - Whether malicious versions could be reinstalled after deletion from a lockfile, local cache or internal mirror: PostHog and GitLab advise clearing caches, but no source addresses this directly; treating it as affected is PlainCVE's judgement. - When each package was actually deleted from npm: PostHog says by 09:30 UTC, but npm registry shows posthog-docusaurus last modified at 09:57 UTC and posthog-plugin-hello-world at 14:11 UTC; the registry does not record the deletion time directly. - Estimates of the whole wave differ: from about 490 to about 800 packages. - No source gives how many times the malicious PostHog versions were downloaded while live. - Most packages, including posthog-docusaurus and @posthog/core, have no individually named official safe version. 「What was breached wasn't PostHog's product, but the automated pipeline that ships it.」— PlainCVE https://plaincve.date/en/vulns/mal-2025-190925-posthog-npm-shai-hulud-2
How to cite this page
This article is CC BY 4.0. Please keep the attribution and link when republishing.
PlainCVE Team (2026). "PostHog's official npm packages hit by the Shai-Hulud 2.0 worm: a misconfigured GitHub workflow handed attackers the publish token". PlainCVE. https://plaincve.date/en/vulns/mal-2025-190925-posthog-npm-shai-hulud-2 (accessed YYYY-MM-DD)BibTeX
@misc{mal2025190925posthognpmshaihulud22026,
title = {PostHog's official npm packages hit by the Shai-Hulud 2.0 worm: a misconfigured GitHub workflow handed attackers the publish token},
author = {PlainCVE Team},
year = {2026},
howpublished = {PlainCVE},
url = {https://plaincve.date/en/vulns/mal-2025-190925-posthog-npm-shai-hulud-2},
note = {Updated 2026-09-30}
}