// threat brief

SAP @cap-js and mbt npm packages hijacked (Mini Shai-Hulud): installing them could steal login keys and spread the worm

CVSS
9.3 CRITICAL
CISA KEV
Not listed
Weakness
CWE-506

PlainCVE Team · Published 2026-09-30 · AI-assisted · Press brief ↓

AI review 2026-09-30 (not human-reviewed)Command tests: none yetReproduced: not yetVerification record ↓

Four SAP developer packages shipped malware on 2026-04-29; if install scripts ran, login keys on the machine or build server could be stolen.

Who is affectedDeveloper machines and CI runners that installed @cap-js/sqlite 2.2.2, @cap-js/postgres 2.2.2, @cap-js/db-service 2.10.1 or mbt 1.2.48 on or after 2026-04-29, including when another package pulled them in indirectly.
What happensA script that runs automatically at install time harvested credentials, and the official advisory says it also "attempted self-propagation". According to researchers, it targeted npm and GitHub tokens, SSH keys, cloud credentials, Kubernetes configs and CI secrets, tried to use stolen npm tokens to infect other packages you can publish, and, when it got a GitHub token with write access to a repository, committed VS Code and Claude Code settings files as a backdoor. When Snyk published, only the original four packages had been seen compromised.
What to do nowUpgrade to @cap-js/db-service 2.11.0, @cap-js/postgres 2.3.0 and @cap-js/sqlite 2.4.0 or later; for mbt, use 1.2.49 and avoid 1.2.48. If a malicious version was ever installed, upgrading is not enough: treat the machine as compromised and, from a different computer, replace every login key (token) and password it could reach.
Affected versions@cap-js/sqlite 2.2.2, @cap-js/postgres 2.2.2, @cap-js/db-service 2.10.1 (GHSA-pvw4-cvr4-97p8 / CVE-2026-46421); mbt 1.2.48 (tracked separately as MAL-2026-3179)
Fixed versions@cap-js/db-service 2.11.0+, @cap-js/postgres 2.3.0+, @cap-js/sqlite 2.4.0+ (maintainers' advisory and CVE description; the structured data in GitHub's global database and OSV says 2.3.0 for sqlite, but sqlite 2.3.0 does not exist on npm); mbt 1.2.49 (a maintainer says in issue #1224 that it is not infected and that npm removed 1.2.48)
CVSS vector
Show full vectorCVSS:4.0/AV:N/AC:L/AT:N/PR:N/UI:N/VC:H/VI:H/VA:H/SC:N/SI:N/SA:N
Timeline
  • Introduced 2026-04-29 · Malicious mbt 1.2.48 published (09:55 UTC) source
  • First public 2026-04-29
  • First attacks 2026-04-29 · Victims' credentials leaked to their public GitHub repos source

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.

DETECTCheckAm I affected? Any signs of compromise?
  1. 01Check whether a malicious version was installed

    Search lockfiles (package-lock.json, npm-shrinkwrap.json, yarn.lock, pnpm-lock.yaml) and CI build logs for @cap-js/sqlite 2.2.2, @cap-js/postgres 2.2.2, @cap-js/db-service 2.10.1 and mbt 1.2.48, including indirect dependencies. @cap-js/sqlite 2.2.2 depended on @cap-js/db-service ^2.10.0, which could pull in the malicious 2.10.1.

    Read-only check
    npm ls @cap-js/sqlite @cap-js/postgres @cap-js/db-service mbt --all
  2. 02Look for the malicious files in node_modules

    The malicious versions contain setup.mjs and execution.js in the package folder. Also watch for a Bun process launched from a temporary directory during npm install.

    Read-only check
    find node_modules/@cap-js node_modules/mbt -maxdepth 2 \( -name setup.mjs -o -name execution.js \) 2>/dev/null
  3. 03Check GitHub for exfiltration and persistence traces

    Look for new public repositories in your account or org described as "A Mini Shai-Hulud has Appeared"; newly added .claude/settings.json or .vscode/tasks.json on any branch; commits by "claude" with unverified signatures; and branches imitating Dependabot's name that add a format-check.yml workflow.

    Read-only check
    git log --all --format='%h %an <%ae> %s' -- .claude/ .vscode/tasks.json .github/workflows/format-check.yml
  4. 04Verify package signatures and provenance

    Useful for @cap-js only. According to SafeDep, the three malicious @cap-js versions lacked the npm provenance attestations that legitimate releases carry, so a missing attestation is a warning sign. Legitimate mbt releases (including 1.2.47 and 1.2.49) have no provenance on npm registry either, so this check cannot tell mbt 1.2.48 apart.

    Read-only check
    npm audit signatures
MITIGATEFix / mitigatePatch; block it if you cannot patch yet
  1. 01Upgrade the @cap-js packages to clean versions

    Use @cap-js/db-service 2.11.0, @cap-js/postgres 2.3.0 and @cap-js/sqlite 2.4.0 or later. The official advisory lists no workarounds. Upgrading does not remove malware that has already run.

    Read-only check
    npm ls @cap-js/sqlite @cap-js/postgres @cap-js/db-service --all
  2. 02Stay off mbt 1.2.48 and use 1.2.49

    In issue

    Read-only check
    npm ls mbt --all
RESPONDRespondIf you find signs of compromise
  1. 01Treat the machine as compromised and rotate from another computer

    Official advice: if a malicious version was installed, rotate every credential that machine could reach. Start with npm tokens, because the worm uses them to spread, then GitHub PATs, cloud credentials, kubeconfigs, Docker configs, SSH keys, password managers and wallets. The stolen data was encrypted, so you can't see what was taken; rotate broadly. Removing the package may not remove the malware.

  2. 02Clean up persistence and attacker artifacts

    Remove IDE and AI-agent persistence files from every branch, delete injected branches and attacker-created repositories, check your npm publish history and unpublish anything you didn't release, and isolate affected CI agents or workspaces.

    Read-only check
    npm view YOUR_PACKAGE time --json
HARDENHardenPrevent the next one
  1. 01Disable install scripts by default and pin versions

    Turn lifecycle scripts off by default and allow them only for packages that need them; pin versions with lockfiles so new releases aren't pulled in automatically.

    Read-only check
    npm config get ignore-scripts
  2. 02Tighten trusted publishing with an approval-gated environment

    Configure trusted publishing with an exact workflow filename and a GitHub deployment environment that requires approval and accepts only protected branches; then set the package to "require two-factor authentication and disallow tokens", and consider staged publishing approved with 2FA.

  3. 03Baseline protection for maintainer accounts and commits

    Use WebAuthn-based 2FA and least-privilege tokens, and verify commit signatures so commits posing as an AI assistant stand out.

Full remediation steps and notes
  1. Check whether you installed it. Search lockfiles and CI build logs for the malicious versions above, including indirect dependencies, or run npm ls @cap-js/sqlite @cap-js/postgres @cap-js/db-service mbt --all. Check those package folders in node_modules for setup.mjs and execution.js.
  2. Upgrade. Use @cap-js/db-service 2.11.0, @cap-js/postgres 2.3.0 and @cap-js/sqlite 2.4.0 or later; for mbt, use 1.2.49 (a maintainer says it is not infected) and don’t use 1.2.48. The official advisory lists no workarounds.
  3. If you installed it, assume compromise. The official advice is to rotate every credential the machine could reach, and to do it from a different computer; removing the package may not remove the malware. Suggested order (Snyk): npm tokens first, since the worm spreads with them, then GitHub PATs, cloud credentials, kubeconfigs, Docker configs, SSH keys, password managers and wallets. The stolen data was encrypted, so you can’t tell what was taken; rotate broadly.
  4. Clean up GitHub (StepSecurity advice). Remove .claude/ and .vscode/tasks.json persistence files from every branch; delete injected branches (including Dependabot look-alikes adding format-check.yml) and repositories described as “A Mini Shai-Hulud has Appeared”; check your npm publish history and unpublish anything you didn’t release. Isolate affected CI agents.
  5. Harden for the long term.
    • Consumers: disable install scripts by default and pin versions with lockfiles; for a package that has always carried provenance (such as @cap-js), treat a new version without it as a warning sign. This cannot catch a package like mbt that never had provenance.
    • Publishers: bind trusted publishing to an exact workflow filename and an approval-gated deployment environment that only accepts protected branches; then set the package to “require two-factor authentication and disallow tokens”, and consider staged publishing approved with 2FA; use WebAuthn 2FA, least-privilege tokens and verified commit signatures.

SAP’s account. In a public fix PR, SAP says an unauthorized actor pushed malicious commits that hijacked the release workflow, which had no manual approval before publishing; after the fix, every npm release needs a human to approve it (PR #1592). In issue #1588, which StepSecurity opened, SAP members replied that clean versions shipped within an hour and the malicious versions were deleted from npm. For mbt, a maintainer explains in issue #1224 that 1.2.49 is not infected, npm confirmed removing 1.2.48, and no GitHub release or Dockerfile was affected. SAP’s full customer guidance is in SAP Note 3747787, which requires a login and could not be read for this page.

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.

SAP @cap-js and mbt npm packages hijacked (Mini Shai-Hulud): installing them could steal login keys and spread the worm: 1. The attacker gains publishing access → 2. The @cap-js release workflow is hijacked (sources disagree on how the versions were published) → 3. Malicious versions go live on npm → 4. The victim runs npm install and the preinstall script fires → 5. Credentials are stolen and sent out → 6. The worm spreads and plants AI-agent persistenceCVE-2026-46421 · SAP @cap-js and mbt npm packages hijacked (Mini Shai-Hulud): installing them could steal login keys and spreadthe wormAttackerYour systemExternalThe attacker gains publishing access — For @cap-js, SAP says only that an unauthorized actor pushed malicious commits that hijacked the release workflow and triggered unauthorized npm publications (PR #1592); the claim that the attacker controlled an SAP developer's GitHub account comes from StepSecurity and SafeDep. For mbt, according to StepSecurity, 1.2.48 was published with a long-lived npm token belonging to the cloudmtabot account; how that token leaked is unconfirmed. 1The attacker gainspublishing accessThe @cap-js release workflow is hijacked (sources disagree on how the versions were published) — In PR 2The @cap-jsrelease workflowis hijacked(sources disagre…Malicious versions go live on npm — The attackers added setup.mjs and execution.js to otherwise normal package contents and changed package.json. According to SafeDep, the three malicious @cap-js versions had no npm provenance attestation, while the previous legitimate releases did (npm registry shows attestations on 2.2.1 and 2.10.0). This does not apply to mbt; npm registry shows no provenance on any mbt version, including 1.2.47 and 1.2.49. 3Malicious versionsgo live on npmThe victim runs npm install and the preinstall script fires — Installing is enough; nothing has to be imported or run. The loader downloads the legitimate Bun runtime and uses it to run an obfuscated payload of about 11 MB. 4The victim runsnpm install andthe preinstallscript firesCredentials are stolen and sent out — The official advisory says it harvested credentials. According to researchers, that includes npm and GitHub tokens, SSH keys, cloud credentials, kubeconfigs and CI secrets, with secrets read straight from runner memory on GitHub Actions. The data is encrypted and pushed to new public repositories created in the victim's own account. 5Credentials arestolen and sent outThe worm spreads and plants AI-agent persistence — The official advisory says only that it "attempted self-propagation". According to StepSecurity's deobfuscation, it uses stolen npm tokens to infect other packages the victim can publish, and uses a stolen GitHub token with write access to commit .claude/settings.json and .vscode/tasks.json, so the payload runs again when the project is opened or an AI coding-agent session starts; the commits pose as authored by "claude". When Snyk published, only the original four packages had been seen compromised. 6The worm spreadsand plantsAI-agentpersistenceAttack pathCan be stopped herePlainCVE · plaincve.date/en/vulns/ghsa-pvw4-cvr4-97p8-sap-cap-js-npm-compromise · CC BY 4.0
SAP @cap-js and mbt npm packages hijacked (Mini Shai-Hulud): installing them could steal login keys and spread the worm: 1. The attacker gains publishing access → 2. The @cap-js release workflow is hijacked (sources disagree on how the versions were published) → 3. Malicious versions go live on npm → 4. The victim runs npm install and the preinstall script fires → 5. Credentials are stolen and sent out → 6. The worm spreads and plants AI-agent persistenceCVE-2026-46421 · SAP @cap-js and mbt npmpackages hijacked (Mini Shai-Hulud):installing them could steal login keysand spread the wormThe attacker gains publishing access — For @cap-js, SAP says only that an unauthorized actor pushed malicious commits that hijacked the release workflow and triggered unauthorized npm publications (PR #1592); the claim that the attacker controlled an SAP developer's GitHub account comes from StepSecurity and SafeDep. For mbt, according to StepSecurity, 1.2.48 was published with a long-lived npm token belonging to the cloudmtabot account; how that token leaked is unconfirmed. 1AttackerThe attacker gainspublishing accessThe @cap-js release workflow is hijacked (sources disagree on how the versions were published) — In PR 2AttackerThe @cap-js releaseworkflow is hijacked(sources disagree on howthe versions werepublished)Malicious versions go live on npm — The attackers added setup.mjs and execution.js to otherwise normal package contents and changed package.json. According to SafeDep, the three malicious @cap-js versions had no npm provenance attestation, while the previous legitimate releases did (npm registry shows attestations on 2.2.1 and 2.10.0). This does not apply to mbt; npm registry shows no provenance on any mbt version, including 1.2.47 and 1.2.49. 3AttackerMalicious versions golive on npmThe victim runs npm install and the preinstall script fires — Installing is enough; nothing has to be imported or run. The loader downloads the legitimate Bun runtime and uses it to run an obfuscated payload of about 11 MB. 4Your systemThe victim runs npminstall and thepreinstall script firesCredentials are stolen and sent out — The official advisory says it harvested credentials. According to researchers, that includes npm and GitHub tokens, SSH keys, cloud credentials, kubeconfigs and CI secrets, with secrets read straight from runner memory on GitHub Actions. The data is encrypted and pushed to new public repositories created in the victim's own account. 5Your systemCredentials are stolenand sent outThe worm spreads and plants AI-agent persistence — The official advisory says only that it "attempted self-propagation". According to StepSecurity's deobfuscation, it uses stolen npm tokens to infect other packages the victim can publish, and uses a stolen GitHub token with write access to commit .claude/settings.json and .vscode/tasks.json, so the payload runs again when the project is opened or an AI coding-agent session starts; the commits pose as authored by "claude". When Snyk published, only the original four packages had been seen compromised. 6ExternalThe worm spreads andplants AI-agentpersistenceAttack pathCan be stopped herePlainCVE · CC BY 4.0plaincve.date/en/vulns/ghsa-pvw4-cvr4-97p8-sap-cap-js-npm-compromise
Free to use in reporting and teaching under CC BY 4.0. Attribution is already on the image.
  1. Attacker

    The attacker gains publishing access

    For @cap-js, SAP says only that an unauthorized actor pushed malicious commits that hijacked the release workflow and triggered unauthorized npm publications (PR #1592); the claim that the attacker controlled an SAP developer's GitHub account comes from StepSecurity and SafeDep. For mbt, according to StepSecurity, 1.2.48 was published with a long-lived npm token belonging to the cloudmtabot account; how that token leaked is unconfirmed.

    Defense: Protect maintainer accounts with WebAuthn-based 2FA; retire long-lived npm tokens in favour of tightly scoped trusted publishing.

  2. Attacker

    The @cap-js release workflow is hijacked (sources disagree on how the versions were published)

    In PR

    Defense: Tie trusted publishing to a GitHub deployment environment that requires approval and only accepts protected branches.

  3. Attacker

    Malicious versions go live on npm

    The attackers added setup.mjs and execution.js to otherwise normal package contents and changed package.json. According to SafeDep, the three malicious @cap-js versions had no npm provenance attestation, while the previous legitimate releases did (npm registry shows attestations on 2.2.1 and 2.10.0). This does not apply to mbt; npm registry shows no provenance on any mbt version, including 1.2.47 and 1.2.49.

    Defense: Pin versions with a lockfile; for a package that has always carried provenance (such as @cap-js), treat a new version without it as a warning sign. This does not help for a package like mbt that never had provenance.

  4. Your system

    The victim runs npm install and the preinstall script fires

    Installing is enough; nothing has to be imported or run. The loader downloads the legitimate Bun runtime and uses it to run an obfuscated payload of about 11 MB.

    Defense: Disable lifecycle scripts by default (for example with --ignore-scripts) and allow them only for packages that truly need them.

  5. Your system

    Credentials are stolen and sent out

    The official advisory says it harvested credentials. According to researchers, that includes npm and GitHub tokens, SSH keys, cloud credentials, kubeconfigs and CI secrets, with secrets read straight from runner memory on GitHub Actions. The data is encrypted and pushed to new public repositories created in the victim's own account.

    Defense: From a different computer, rotate every credential the machine could reach, starting with npm tokens.

  6. External

    The worm spreads and plants AI-agent persistence

    The official advisory says only that it "attempted self-propagation". According to StepSecurity's deobfuscation, it uses stolen npm tokens to infect other packages the victim can publish, and uses a stolen GitHub token with write access to commit .claude/settings.json and .vscode/tasks.json, so the payload runs again when the project is opened or an AI coding-agent session starts; the commits pose as authored by "claude". When Snyk published, only the original four packages had been seen compromised.

    Defense: Require verified commit signatures; review any new .claude/ or .vscode/ files on every branch.

Who is affected

This is not a coding bug. Official SAP packages were swapped for malicious versions (CWE-506, Embedded Malicious Code). Two related but separate compromises happened on the same day: the three @cap-js packages (GHSA-pvw4-cvr4-97p8 / CVE-2026-46421), and mbt (tracked separately as MAL-2026-3179 and not part of that CVE).

PackageMalicious versionClean / fixed versionTracking ID
@cap-js/db-service2.10.12.11.0 or laterGHSA-pvw4-cvr4-97p8
@cap-js/postgres2.2.22.3.0 or laterGHSA-pvw4-cvr4-97p8
@cap-js/sqlite2.2.22.4.0 or later (maintainers’ advisory, CVE description; GitHub’s global database and OSV say 2.3.0, a version that does not exist on npm)GHSA-pvw4-cvr4-97p8, MAL-2026-3178
mbt1.2.481.2.49 (a maintainer says in issue #1224 it is not infected; 1.2.47 is also not infected)MAL-2026-3179
SituationAffected?
Ran npm install on or after 2026-04-29 and resolved any malicious version above (developer machine or CI)Affected; treat it as compromised
Depended on @cap-js/sqlite 2.2.2, which pulled in @cap-js/db-service 2.10.1 indirectlyAffected (sqlite 2.2.2 depended on db-service ^2.10.0)
Installed with lifecycle scripts disabled (for example —ignore-scripts)By the mechanism, the hook would not run, but no advisory states this as a guarantee
Lockfile pinned to earlier versions and not updatedBy how npm works, the new versions would not be installed; the advisory does not say so explicitly

For scale: Socket puts weekly downloads at about 260k for db-service, 250k for sqlite, 52k for mbt and 10k for postgres. SafeDep estimates about 1.2 million installs per month across the four packages.

How it works

How the attackers got publishing rights.

  • @cap-js: In PR #1592, SAP says an unauthorized actor pushed malicious commits that hijacked the release workflow and triggered unauthorized npm publications, and that there was no manual approval before npm publishing. SAP does not say whether that publishing used trusted publishing (OIDC) or an npm token, and researchers give different accounts:
    • StepSecurity and SafeDep say the attacker controlled an SAP developer’s GitHub account, changed the release workflow so it would run from a non-main branch, used the CI job’s OIDC identity to obtain npm publish credentials through trusted publishing, printed them to the build log in encoded form, and published with them.
    • Snyk, citing npm registry data, says all four malicious versions (including the three @cap-js ones) were published from the cloudmtabot account; OSV’s MAL-2026-3178 and MAL-2026-3179 also say they were published by “cloudmtabot@gmail.com”.
    • SAP has not said publicly which is correct, and this page cannot settle it.
  • mbt: According to StepSecurity, 1.2.48 was published with a long-lived npm token for the cloudmtabot account; mbt never used trusted publishing. How that token was stolen is unconfirmed. BleepingComputer cites security engineer Adnan Khan as saying an npm token in SAP’s npm publishing process may have been exposed through a misconfigured CircleCI job, without naming the package; no source confirms that this was the mbt token.

The configuration gap. npm trusted publishing ties publishing rights to an organization, a repository and an exact workflow filename. A GitHub deployment environment (which can require approval) is optional. SafeDep and StepSecurity say the trust here was not limited to a protected branch or an approval-gated environment, so a workflow run from the attacker’s own branch could still get publish credentials. SAP’s exact configuration could not be confirmed for this page. If the cloudmtabot publishing that Snyk and OSV describe is what actually happened, this gap was not the direct cause, although SAP did add a manual approval step before publishing afterwards.

The flawed approach (conceptual illustration, not SAP’s actual configuration):

Show code example(yaml)
# npm trusts: this repo + release.yml
# No environment binding → release.yml on any branch can get publish credentials
jobs:
  publish:
    permissions:
      id-token: write

The fixed approach:

Show code example(yaml)
# npm trusts: this repo + release.yml + environment "npm-release"
# Environment rules live in repo settings (editing the workflow can't change them):
#   only the protected main branch, maintainer approval required
jobs:
  publish:
    environment: npm-release
    permissions:
      id-token: write

The key point: writing “only run on main” inside the workflow file isn’t enough, because the attacker can edit that file. The restriction has to live somewhere the attacker can’t change, such as the environment’s protection rules.

What the malicious code did. The official advisory says only that it harvested credentials and “attempted self-propagation”; the details below come from researchers such as StepSecurity, SafeDep and Snyk.

  1. The malicious versions added setup.mjs and execution.js to otherwise normal package contents and changed package.json. According to SafeDep, the three malicious @cap-js versions had no npm provenance attestation, while the previous legitimate releases did; no mbt version on npm registry has ever carried provenance, so this applies to @cap-js only.
  2. setup.mjs ran as a preinstall script, so installing was enough; nothing had to be imported. It downloaded the legitimate Bun runtime and used it to run an obfuscated payload of about 11 MB (OSV says 11.1 MB, StepSecurity says 11.6 MB).
  3. It stole npm and GitHub tokens, SSH keys, AWS/Azure/GCP credentials and secrets-manager access, Kubernetes configs and CI/CD secrets, plus AI tool configs and crypto wallets. On GitHub Actions it read secrets directly from the runner process’s memory, getting around log masking.
  4. It encrypted the data and committed it to new public repositories in the victim’s own account, described as “A Mini Shai-Hulud has Appeared”. SafeDep also describes an HTTPS command-and-control endpoint, so the reports differ in detail.
  5. Self-propagation: according to StepSecurity’s deobfuscation, it uses stolen npm tokens to publish infected versions of other packages the victim can publish, and uses a stolen GitHub token with write access to commit persistence files into repositories (injecting a workflow also needs a token with workflow scope). When Snyk published, it had seen only the original four packages compromised.

AI coding agents turned into a backdoor. According to StepSecurity and SafeDep, when the worm got a GitHub token with the right permissions it committed .claude/settings.json (with a SessionStart hook) and .vscode/tasks.json (set to run when a folder opens), so the payload ran again whenever a developer opened the project or started an AI coding-agent session. The commits appeared under the author “claude” with unverified signatures, looking like routine AI-assistant changes. StepSecurity calls this one of the first supply-chain attacks to abuse AI-agent configuration this way. The attackers also pushed a branch named to imitate Dependabot, carrying a workflow meant to dump repository secrets. StepSecurity and Socket report a kill switch that stops the malware on Russian-locale systems.

Timeline

Time (2026, UTC)Event
04-29 09:55mbt 1.2.48 published via cloudmtabot
04-29 ~11:23First malicious commit to cap-js/cds-dbs on branch update/releases
04-29 ~11:25–12:14The three malicious @cap-js versions are published; the workflow is then cancelled and the branch force-reverted (exact per-package order unconfirmed)
04-29StepSecurity, SafeDep, Socket, Snyk and BleepingComputer publish reports; issue #1224 opened on the mbt repo; OSV publishes MAL-2026-3178 and MAL-2026-3179
04-29 ~13:46Clean releases published via GitHub Actions trusted publishing (sqlite 2.4.0, postgres 2.3.0, db-service 2.11.0)
04-29 16:15mbt 1.2.49 published (a maintainer says it is not infected)
04-30GHSA-2h7r-x9v2-q52f (“Malware in @cap-js/sqlite”) published; the mbt maintainer says npm confirmed removing 1.2.48
05-14Maintainers publish repository advisory GHSA-pvw4-cvr4-97p8
05-20The advisory appears in GitHub’s global database and OSV
07-15GitHub (CNA) publishes CVE-2026-46421; NVD adds it the same day with status Deferred
—Not added to CISA KEV

Further reading

  • Identifier correction. The incident record this page started from said there was no CVE, but GitHub has assigned CVE-2026-46421 to GHSA-pvw4-cvr4-97p8. It is still not in CISA KEV. The CVE covers only the three @cap-js packages; mbt was a separate compromise carried out a different way.
  • Scoring. The only score is GitHub’s (CNA) CVSS 4.0 9.3 Critical. NVD has not scored it (Deferred), and no CVSS 3.x score was found. CISA’s SSVC field in the CVE record says Exploitation “none”; that is a data field, not a statement that no attack happened. Researchers watched this malware spread in the wild.
  • Date basis. This page gives 2026-04-29 as the disclosure date: the day the first malicious version (mbt 1.2.48, 09:55 UTC) appeared on npm. For supply-chain compromises, this site always uses the date the malicious versions appeared. The maintainers’ advisory (05-14) and the CVE (07-15) came later.
  • Where sources disagree. On how the @cap-js versions were published, SAP says only that the hijacked workflow triggered unauthorized npm publications; StepSecurity and SafeDep say credentials were obtained through trusted publishing (OIDC), while Snyk and OSV say the cloudmtabot account published them. On the fixed @cap-js/sqlite version, the maintainers’ advisory and the CVE description say 2.4.0 or later, while the structured data in GitHub’s global database and OSV says 2.3.0, a version that never appeared on npm. The payload size is given as both 11.1 MB and 11.6 MB. For the exfiltration channel, StepSecurity names GitHub’s API while SafeDep describes an HTTPS command-and-control endpoint.
  • Campaign background. The first Shai-Hulud wave (September 2025) was a self-copying npm worm that ran from install scripts. The second (November 2025, Shai-Hulud 2.0) moved to preinstall with a Bun loader, and Unit 42 counted more than 25,000 malicious repositories. StepSecurity calls this incident the third wave.
  • Attribution is a researcher assessment only. Socket links it with medium confidence to TeamPCP (shared cipher, Russian-locale kill switch, GitHub abused for command and control). StepSecurity points to Eastern European or Russian operators. There is no official attribution.
  • File hashes and further network indicators are in StepSecurity’s report; the official advisories don’t publish them, so this page doesn’t reproduce them.
  • Related weakness type: CWE-506 (Embedded Malicious Code).

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 review2026-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

  1. Patch / releaseci: separate release-please from npm publish job (PR #1592) · cap-js / SAP (GitHub), 2026-05-04 · accessed 2026-09-28
    SAP's own root-cause statement - unauthorized commits hijacked the release workflow, which had no manual approval before publishing.
  2. Other@cap-js/sqlite@2.2.2, @cap-js/db-service@2.10.1, and @cap-js/postgres@2.2.2 npm releases are compromised (issue #1588) · GitHub (cap-js/cds-dbs issue opened by StepSecurity), 2026-04-29 · accessed 2026-09-28
    The issue StepSecurity used to report the compromise; it contains replies from SAP members (chgeo, patricebender).
  3. AdvisorySupply chain compromise via malicious package versions (@cap-js/sqlite, @cap-js/postgres, @cap-js/db-service) · cap-js / SAP (GitHub), 2026-05-14 · accessed 2026-09-23
  4. AdvisoryGHSA-pvw4-cvr4-97p8 / CVE-2026-46421 · GitHub Advisory Database, 2026-05-20 · accessed 2026-09-28
    The global database's structured data gives 2.3.0 as the fixed version for @cap-js/sqlite, unlike the 2.4.0 in the maintainers' advisory.
  5. AdvisoryGHSA-pvw4-cvr4-97p8 · OSV, 2026-05-20 · accessed 2026-09-23
  6. AdvisoryCVE-2026-46421 record · CVE Program (CNA: GitHub), 2026-07-15 · accessed 2026-09-23
  7. CVE / NVD / OSVCVE-2026-46421 · NIST NVD, 2026-07-15 · accessed 2026-09-23
  8. AdvisoryMAL-2026-3178: @cap-js/sqlite · OSV / OpenSSF Malicious Packages, 2026-04-29 · accessed 2026-09-23
  9. AdvisoryMAL-2026-3179: Malicious code in mbt (npm) · OSV / OpenSSF Malicious Packages, 2026-04-29 · accessed 2026-09-23
  10. AdvisoryMalware in @cap-js/sqlite · GitHub Advisory Database, 2026-04-30 · accessed 2026-09-23
  11. Othermbt v1.2.48 is compromised · Issue #1224 · GitHub (SAP/cloud-mta-build-tool issue opened by StepSecurity), 2026-04-29 · accessed 2026-09-28
    In a 2026-04-30 reply, maintainer kbarnold says 1.2.47 and 1.2.49 are not infected and that npm removed 1.2.48.
  12. Othermbt package metadata · npm registry · accessed 2026-09-28
  13. Other@cap-js/sqlite package metadata · npm registry · accessed 2026-09-23
  14. ResearchA Mini Shai-Hulud has Appeared · StepSecurity, 2026-04-29 · accessed 2026-09-23
  15. ResearchMini Shai-Hulud and SAP Compromise · SafeDep, 2026-04-29 · accessed 2026-09-28
  16. ResearchTeamPCP-Linked Supply Chain Attack Hits SAP CAP and Cloud MTA npm Packages · Socket, 2026-04-29 · accessed 2026-09-23
  17. ResearchBun-Based Stealer Hits SAP @cap-js and mbt npm Packages · Snyk, 2026-04-29 · accessed 2026-09-23
  18. NewsOfficial SAP npm packages compromised to steal credentials · BleepingComputer, 2026-04-29 · accessed 2026-09-28
  19. Research"Shai-Hulud" Worm Compromises npm Ecosystem in Supply Chain Attack · Palo Alto Networks Unit 42, 2025-11-26 · accessed 2026-09-23
  20. VendorTrusted publishing for npm packages · npm Docs · accessed 2026-09-23

Press brief

In one sentence

On April 29, 2026, attackers published malicious versions of three official SAP CAP database packages (@cap-js) and the SAP MTA build tool mbt on npm. Installing them was enough to steal credentials from developer and CI machines, and the official advisory says the malware also attempted self-propagation. Researchers call it Mini Shai-Hulud, the third wave of the Shai-Hulud npm worm, and report that when it got a GitHub token with the right permissions it planted Claude Code and VS Code settings files so the malware would run again when a project was opened or an AI coding agent started. The @cap-js part has been assigned CVE-2026-46421, and clean versions were released the same day.

Key facts

Identifiers
GHSA-pvw4-cvr4-97p8 / CVE-2026-46421 (covers the three @cap-js packages only); mbt is tracked as MAL-2026-3179 cveawg.mitre.org
Severity
CVSS 4.0 9.3 Critical (CNA: GitHub); not scored by NVD (Deferred) services.nvd.nist.gov
Malicious versions
@cap-js/sqlite 2.2.2, @cap-js/postgres 2.2.2, @cap-js/db-service 2.10.1 github.com
Date
2026-04-29 (first malicious version, mbt 1.2.48, published at 09:55 UTC; this page uses the date the malicious versions appeared as the disclosure date) stepsecurity.io
Attribution (researcher assessment)
Socket links it to TeamPCP with medium confidence socket.dev

Confirmed

  • @cap-js/sqlite 2.2.2, @cap-js/postgres 2.2.2 and @cap-js/db-service 2.10.1 are malicious; the maintainers' advisory gives the fixed versions as sqlite 2.4.0, postgres 2.3.0 and db-service 2.11.0 or later (GitHub's global database and OSV list 2.3.0 for sqlite, a version that does not exist on npm).
  • mbt 1.2.48 is listed as malicious (MAL-2026-3179) and has been removed from npm; a maintainer says 1.2.49 is not infected.
  • The malware runs automatically at install time (preinstall), and the official advice is to rotate every credential an affected machine could reach.
  • The incident is not listed in CISA's Known Exploited Vulnerabilities (KEV) catalog.

Unconfirmed / disputed

  • How the cloudmtabot npm token for mbt was stolen is unknown. BleepingComputer cites security engineer Adnan Khan as saying an npm token in SAP's npm publishing process may have been exposed through a misconfigured CircleCI job, without naming which package's token; any link to mbt is unconfirmed.
  • How the @cap-js versions were published: StepSecurity and SafeDep say the hijacked workflow obtained npm credentials through trusted publishing (OIDC); Snyk, citing npm registry data, and the OSV MAL records say they were published from the cloudmtabot account.
  • There is no official data on whether the malware actually infected any packages beyond the original four; when Snyk published, only those four had been seen.
  • SAP's customer note (SAP Note 3747787) requires a login and could not be read; how the mbt token leaked and its follow-up are unconfirmed.
  • There is no official victim count. Researchers searched GitHub for the marker repositories; Snyk counted 1,076 at 15:17 UTC on April 29, and SafeDep reported over 1,000 that day.

Quotable line

SAP's release workflow could publish to npm without any human approval, so once an unauthorized actor's commits hijacked it, the malicious versions went straight out.

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.

How to cite this page

This article is CC BY 4.0. Please keep the attribution and link when republishing.

PlainCVE Team (2026). "SAP @cap-js and mbt npm packages hijacked (Mini Shai-Hulud): installing them could steal login keys and spread the worm". PlainCVE. https://plaincve.date/en/vulns/ghsa-pvw4-cvr4-97p8-sap-cap-js-npm-compromise (accessed YYYY-MM-DD)
BibTeX
@misc{ghsapvw4cvr497p8sapcapjsnpmcompromise2026,
  title  = {SAP @cap-js and mbt npm packages hijacked (Mini Shai-Hulud): installing them could steal login keys and spread the worm},
  author = {PlainCVE Team},
  year   = {2026},
  howpublished = {PlainCVE},
  url    = {https://plaincve.date/en/vulns/ghsa-pvw4-cvr4-97p8-sap-cap-js-npm-compromise},
  note   = {Updated 2026-09-30}
}