critical

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-bigint
  • sort-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 packageLegitimate tool it can be confused withWhy the confusion works
transform-jsbi-to-bigintbabel-plugin-transform-jsbi-to-bigintBabel 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-autofixeslint-plugin-sort-imports-es6-autofixThe 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, and npm_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:

  1. machines and CI images that still run older npm clients
  2. developers who approve the blocked script because the package name looks familiar
  3. 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.

  1. isolate the affected workstation or runner
  2. rotate npm, GitHub, GitLab, Jenkins, CircleCI, cloud, SSH, and other reachable secrets from a clean machine
  3. review package-publishing history and CI workflow changes for follow-on abuse
  4. rebuild the environment from a known-good base image or machine state
  5. keep --ignore-scripts or 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