CVE
Not assigned
CWE
CWE-506, CWE-829, CWE-522
Affected Surface
- npm package `transform-jsbi-to-bigint@9.9.0`
- npm package `sort-imports-es6-autofix`, which public malware advisories treat as unsafe with no patched release
- Developer workstations and CI runners that installed those packages or approved blocked install scripts for their fetched dependencies
- Organizations whose Git, npm, GitHub Actions, GitLab CI, Jenkins, CircleCI, or cloud-access secrets were reachable from the infected install context
The most useful npm supply-chain report from the last three days is not about a brand-new package wave landing on the registry this morning. It is a 15 September incident writeup that ties real compromises to two PhantomRaven package names and, more importantly, shows the delivery trick clearly enough to hunt for it in code and build logs.
The two package names CrowdStrike published were:
transform-jsbi-to-bigintsort-imports-es6-autofix
Both names sit close to real developer tooling. That matters because the social-engineering layer here is simple. The attacker did not need a package name that looked suspicious. They only needed one that looked plausible in a Babel or ESLint setup.
| Malicious package | Legitimate tool it can be confused with | Why the confusion works |
|---|---|---|
transform-jsbi-to-bigint | babel-plugin-transform-jsbi-to-bigint | Babel plugins are commonly referenced by shortened names in config and docs, so a developer can recognize the suffix and miss the missing babel-plugin- prefix. |
sort-imports-es6-autofix | eslint-plugin-sort-imports-es6-autofix | The malicious package drops the eslint-plugin- prefix from a real linting plugin name that many developers already know. |
The package page is not the payload
PhantomRaven’s most useful trick is not obfuscation. It is indirection.
According to CrowdStrike and earlier Endor Labs analysis, the published npm package can stay almost empty while package.json points npm at an attacker-controlled HTTP dependency. npm then fetches that remote tarball during a normal install, and the fetched tarball contains the real stealer.
Reduced to the execution path defenders care about, it looks like this:
npm install malicious-package
-> npm reads package.json
-> package.json includes a dependency hosted at an attacker URL
-> npm fetches that tarball outside the normal registry path
-> fetched tarball runs preinstall: node index.js
-> index.js reads local config and CI/CD environment data
-> collected data is exfiltrated over HTTP
That is why this campaign kept showing up in public reporting even when the package contents looked trivial. The interesting file was not always the package tarball the developer thought they were reviewing. The real execution path lived one fetch later.
Endor Labs gave this pattern a name earlier in the year: Remote Dynamic Dependencies, or RDD. The detail worth remembering is not the label. It is the trust boundary failure. Many dependency review workflows still assume “inspect the package on the registry” is close to “inspect the code that will run.” PhantomRaven breaks that assumption.
What the stealer actually reached for
CrowdStrike’s list of collected fields is concrete enough to show why this matters to AppSec teams and not just desktop-response teams.
The payload gathered:
- host details such as OS, architecture, hostname, Node version, process ID, working directory, and local and external IP addresses
- username and email data pulled from Git and npm configuration sources
- environment variables that identify or support CI platforms, including GitHub Actions, GitLab CI, Jenkins, and CircleCI
- package and registry context such as
npm_config_registry,npm_package_name, andnpm_package_version
A reduced view of the interesting environment slice looks like this:
GITHUB_ACTIONS
GITHUB_ACTOR
GITHUB_REPOSITORY
GITHUB_RUN_ID
GITLAB_CI
CI_PROJECT_PATH
JENKINS_URL
JOB_NAME
CIRCLECI
CIRCLE_BUILD_URL
npm_config_registry
That is enough to turn “a developer installed a bad package” into “a build runner exposed package-publishing or source-control credentials.” PhantomRaven was not chasing only local shell access. It was chasing whatever identity the install happened to inherit.
CrowdStrike also says the code queried https://api64.ipify.org?format=json for an external IP and then exfiltrated the collected data through HTTP GET and POST requests, with an unfinished WebSocket fallback left in the code. The fallback matters less than the larger pattern: the malware expected to run in environments where outbound HTTP was still possible during installs.
The current registry state only shows the cleanup
If you check the live npm packuments today, both package names resolve to security placeholder releases rather than the original malicious content:
transform-jsbi-to-bigint -> latest: 0.0.1-security
sort-imports-es6-autofix -> latest: 0.0.1-security
That cleanup state is useful, but only in a narrow way. It tells you the package names were already recognized as malicious and replaced. It does not tell you which developer machines or CI runners fetched the bad versions before the registry was cleaned up.
For transform-jsbi-to-bigint, public malware reporting ties the malicious version to 9.9.0. For sort-imports-es6-autofix, the public malware advisory treats the package as unsafe and does not offer a clean upgrade line. In both cases, the operational response is the same: scope exposure by install history and cached artifacts, not by what the registry page shows now.
npm 12 helps, but it does not erase the exposure window
One reason this September writeup matters is that it lands after npm changed install-script handling in June 2026.
CrowdStrike notes that npm 12 blocks dependency install scripts by default unless the developer explicitly approves them. The warning looks roughly like this:
npm warn install-scripts 1 package had install scripts blocked because they are not covered by allowScripts:
npm warn install-scripts [package]@1.0.0 (preinstall: node index.js)
npm warn install-scripts
npm warn install-scripts Run `npm install-scripts ls` to review, or `npm install-scripts approve <pkg>` to allow.
That is real progress. It is also not the same thing as immunity.
Three exposure paths still matter:
- machines and CI images that still run older npm clients
- developers who approve the blocked script because the package name looks familiar
- environments where a historical install already ran before the newer npm behavior was in place
The broader lesson is that lifecycle-script controls help only when the operator notices the warning and treats the package name itself as untrusted input.
This was not a two-package story
The fresh CrowdStrike writeup names two packages tied to real incidents. It does not mean the campaign stopped at two names.
Earlier public reporting had already linked PhantomRaven to a much larger package set:
- Wave 1: more than 200 malicious packages according to the earlier public count summarized by Endor Labs and BleepingComputer
- Waves 2 through 4: 88 additional packages identified by Endor Labs
- Wave 5: 33 more named packages documented later by Mend
To avoid overstating what the public sources explicitly enumerate, here are the package names we were able to verify directly from the cited campaign writeups:
Wave 1 examples:
@dtpk-cc/components, airbnb-flow, unused-imports
Wave 2 named packages:
transform-react-jsx, syntax-decorators, syntax-exponentiation-operator,
transform-dev, minify-mangle-names, typescript-resolvers,
typescript-react-query, typescript-rtk-query, es6-recommended, prefer-let,
developit, jam3, yoshi-base, vitest-globals, @storylane/uikit,
@storylane/shared-packages
Wave 3 named packages:
google-camelcase, transform-typescript, transform-spread, transform-for-of,
transform-new-target, transform-dynamic-import, syntax-async-generators,
syntax-function-bind, syntax-do-expressions, syntax-export-extensions,
add-react-displayname
Wave 4 named packages:
sort-export-all, filter-imports, mui-path-imports, import-zod
Wave 5 named packages:
eigenlayer-sdk, griffing-ui-pkg, griffing-ux-pkg, rampage-unpack,
random-unpack, return-words, return-npm,
transform-es3-member-expression-literals, digitalexp-common-components-l9,
link-ui-pkg, react-native-wcandillondigital-sdk, graphql-js-client-transform,
react-remove-properties, react-schedule-it, adb-node,
@cdktf-constructs/azure-publicipaddress,
@cdktf-constructs/azure-resourcegroup,
@cdktf-constructs/azure-subnet,
@cdktf-constructs/azure-virtualnetworkgateway,
@cdktf-constructs/azure-virtualnetworkgatewayconnection,
@promptions/promptions-chat, @promptions/promptions-image,
@promptions/promptions-llm, @promptions/promptions-ui,
@inverse-finance/vesting-contracts, polyfill-es-shims,
transform-es2015-destructuring, transform-es2015-typeof-symbol, escompat,
mistica-local-rules, transform-es2015-computed-properties,
transform-es2015-template-literals, export-default
The two September package names, transform-jsbi-to-bigint and sort-imports-es6-autofix, do not appear in those earlier public package lists. That is useful context. It means the latest report is not just retelling the older writeups. It adds incident-response evidence and additional package aliases tied to the same malware family.
What to check in real repositories and runners
Start with dependency files, lockfiles, and install logs:
rg -n \
'transform-jsbi-to-bigint|sort-imports-es6-autofix|npm\.jpartifacts\.com|packages\.storeartifact\.com|storeartifacts\.com|artifactsnpm\.com' \
package.json package-lock.json pnpm-lock.yaml yarn.lock bun.lock .npmrc . 2>/dev/null
Then hunt for the CI and credential context PhantomRaven was built to steal:
rg -n \
'GITHUB_ACTIONS|GITHUB_RUN_ID|GITLAB_CI|CI_PROJECT_PATH|JENKINS_URL|CIRCLECI|npm_config_registry' \
.github .gitlab-ci.yml Jenkinsfile .circleci . 2>/dev/null
If you keep install logs or shell history from affected runners, search for approval events as well:
rg -n \
'npm install-scripts approve|install scripts blocked|preinstall: node index.js' \
~/.npm/_logs . 2>/dev/null
For incident scoping, the key question is not only “Did we ever depend on this package name?” It is also “Did any machine with source-control, registry, or CI secrets install it while those secrets were in scope?”
Response guidance
Treat any confirmed install of the malicious packages as a credential compromise, not as a dependency hygiene bug.
- isolate the affected workstation or runner
- rotate npm, GitHub, GitLab, Jenkins, CircleCI, cloud, SSH, and other reachable secrets from a clean machine
- review package-publishing history and CI workflow changes for follow-on abuse
- rebuild the environment from a known-good base image or machine state
- keep
--ignore-scriptsor npm 12 script approvals locked down in CI, and avoid approving install scripts for unfamiliar or merely plausible package names
The main lesson from PhantomRaven is not subtle. Package review that stops at the registry page is no longer enough. If your install path can fetch code from somewhere else, then the package manager itself becomes the loader, and npm install becomes part of your attack surface.
From research to remediation
Check whether this pattern exists in your codebase
Turn this research into a remediation workflow. Scan dependencies and package manifests for similar supply-chain risk, then prioritize fixes with reachability context.
References
- CrowdStrike: PhantomRaven
- Endor Labs: The Return of PhantomRaven
- BleepingComputer: New PhantomRaven NPM attack wave steals dev data via 88 packages
- Mondoo MAL-2026-1504: transform-jsbi-to-bigint
- npm registry metadata: transform-jsbi-to-bigint
- npm registry metadata: sort-imports-es6-autofix
- Legitimate package: babel-plugin-transform-jsbi-to-bigint
- Legitimate package: eslint-plugin-sort-imports-es6-autofix