On 29 April 2026, malicious versions of mbt and three @cap-js database packages from SAP's open source tooling ran a credential stealer from a preinstall hook. Any laptop or CI runner that installed mbt@1.2.48, @cap-js/sqlite@2.2.2, @cap-js/postgres@2.2.2 or @cap-js/db-service@2.10.1 should be treated as compromised. Check lockfiles and CI logs, rotate every reachable secret, then upgrade.
This was months ago, but lockfiles from late April still sit in old branches, CI caches and container layers. Below: the verified facts, how to prove whether you installed a bad version, what to rotate, and what would have helped. Safeguard does not detect malicious packages; its part is inventory and response.
What happened
The four packages belong to SAP's developer tooling. mbt is the Cloud MTA Build Tool used to build multitarget application archives. The @cap-js packages are database adapters for the SAP Cloud Application Programming Model (CAP), which Onapsis describes as the de facto framework for custom development on SAP BTP. Socket put weekly downloads at roughly 260,000 for @cap-js/db-service, 250,000 for @cap-js/sqlite, 52,000 for mbt and 10,000 for @cap-js/postgres.
The bad versions arrived by two different routes, a detail that matters for the lessons later.
- mbt was published from the
cloudmtabotnpm account. StepSecurity says the package did not use OIDC trusted publishing, so the attacker most likely used a stolen long-lived npm automation token. BleepingComputer cites security engineer Adnan Khan's report that a token may have been exposed through a misconfigured CircleCI job. The exact channel has not been confirmed. - The three @cap-js packages were published through the project's own GitHub Actions release path. According to StepSecurity and The Hacker News, a developer's GitHub account was compromised and commits were pushed to a non-main branch of
cap-js/cds-dbs. SafeDep, as quoted by The Hacker News, said the npm trusted publisher setting trusted any workflow in the repository, not only the release workflow onmain. A branch push could therefore obtain a publish token. SAP's own fix PR says the workflow had publish rights with no manual approval gate.
Timeline
All times UTC. Registry times come from the npm registry metadata we fetched on 22 October 2026.
| Date and time | Event | Source |
|---|---|---|
| 24 Apr, 11:29 | mbt@1.2.47 published (last version before the incident) | npm registry |
| 29 Apr, 09:55:25 | mbt@1.2.48 published from the cloudmtabot account | npm registry, Snyk, StepSecurity |
| 29 Apr, 10:01:07 | First victim dead drop repository appears on GitHub | Snyk |
| 29 Apr, about 11:23 | First malicious commit pushed to a release branch of cap-js/cds-dbs | StepSecurity |
| 29 Apr, 11:25:47 | @cap-js/sqlite@2.2.2 published | npm registry |
| 29 Apr, 12:14 | @cap-js/postgres@2.2.2 and @cap-js/db-service@2.10.1 published | Onapsis, Snyk |
| 29 Apr, 13:33 to 13:46 | Clean @cap-js releases published through SAP's OIDC trusted publisher, including @cap-js/sqlite@2.4.0 at 13:46 | Onapsis, Snyk, npm registry |
| 29 Apr, 16:15:54 | mbt@1.2.49 published; maintainers call it "not infected" and say it was released manually | npm registry, GitHub issue #1224 |
| 30 Apr, 02:33 | npm confirms mbt@1.2.48 removed, per the maintainer | GitHub issue #1224 |
| 30 Apr | SAP releases Security Note 3747787 covering the four packages | Onapsis |
| 4 May | SAP merges PR #1592: npm publishing now needs a human approval, and workflows are pinned to commit SHAs | cap-js/cds-dbs PR #1592 |
| 14 May | Advisory GHSA-pvw4-cvr4-97p8 (CVE-2026-46421) published on cap-js/cds-dbs | GitHub |
| 19 May | A separate compromised release, @cap-js/openapi@1.4.1, is published; fixed in 1.4.2 | GitHub advisory GHSA-jpvj-wpmj-h7rv |
| 20 May | GHSA-pvw4-cvr4-97p8 reviewed in the GitHub Advisory Database | GitHub |
What SAP has said publicly
SAP's formal guidance is Security Note 3747787, which sits behind an SAP login, and a linked SAP document that returned an access error when we tried to fetch it. The public record from SAP's own engineers is in GitHub:
- In the
mbtissue, a maintainer posted the release times above and said only the npm package was affected, with no GitHub or Dockerfile release of 1.2.48. - In
cds-dbs, the fix PR says an unauthorized actor pushed commits that hijacked the release workflow, and moves publishing into a job a reviewer must approve. - The
cds-dbsadvisory says to treat all credentials on any machine that installed a bad version as compromised: npm tokens, cloud credentials, SSH keys and GitHub personal access tokens. It lists no workarounds.
Attribution
Wiz said the packages match several features of earlier TeamPCP operations, including a shared RSA public key, according to The Hacker News. Socket and BleepingComputer link it to TeamPCP with medium confidence. Researchers call it "Mini Shai-Hulud," the same phrase the malware writes on its exfiltration repositories. For the family's history, see our Shai-Hulud explainer.
Who is affected
| Package | Malicious version | Move to | Notes | Source |
|---|---|---|---|---|
mbt | 1.2.48 | 1.2.49 or later (latest is 1.2.49) | Removed from the registry; 1.2.49 was released manually and maintainers describe it as functionally identical to 1.2.47 | npm registry, issue #1224 |
@cap-js/sqlite | 2.2.2 | 2.4.0 or later | Removed from the registry | GHSA-pvw4-cvr4-97p8, npm registry |
@cap-js/postgres | 2.2.2 | 2.3.0 or later | GHSA-pvw4-cvr4-97p8 | |
@cap-js/db-service | 2.10.1 | 2.11.0 or later | Can arrive transitively through @cap-js/sqlite@2.2.2 | GHSA-pvw4-cvr4-97p8, Snyk |
@cap-js/openapi | 1.4.1 (19 May, separate event) | 1.4.2 or later | CVSS 3.1 base 9.6 | GHSA-jpvj-wpmj-h7rv |
Snyk notes that @cap-js/sqlite@2.2.2 declares @cap-js/db-service@^2.10.0, so a project that never listed db-service could still resolve 2.10.1 during the window. Snyk also observed that while it was writing, mbt@1.2.48 was still the latest tag, so a bare npm install mbt got the bad tarball.
Onapsis says the most exposed are Node.js apps on SAP BTP, CAP and Fiori backends, and MTA projects pulling @sap/* and @cap-js/* with loose ranges. One user asked in the mbt issue whether the mbt preinstalled in SAP Business Application Studio was affected; the thread has no answer, so check it directly.
How it works
No exploit details here, only what you need to recognise it and respond.
The install hook
Each malicious release added "preinstall": "node setup.mjs" to its manifest. StepSecurity notes that clean mbt@1.2.47 has no scripts block at all, so a new install hook was itself the signal. npm runs lifecycle scripts automatically, so the code ran before any of your code and whether or not you ever imported the package. Onapsis points out that because it runs at preinstall, credentials may be stolen even if the install fails or is cancelled.
setup.mjs is a small loader. Per Socket and StepSecurity, it checks whether Bun is on PATH, downloads Bun 1.3.13 from GitHub Releases if not, and uses it to run execution.js, an obfuscated single line file of about 11.6 MB. Running under Bun keeps it away from tooling that only watches Node.
What it takes
Across StepSecurity, Socket, Snyk and The Hacker News, the payload:
- Harvests npm and GitHub tokens (including the output of
gh auth token), AWS, Azure and GCP credentials and the contents of AWS Secrets Manager and GCP Secret Manager it can reach, Kubernetes configs, SSH keys, Docker and git credentials,.npmrc,.envfiles, shell history, crypto wallets, VPN profiles and messaging app data. - Reads AI tool configs such as
~/.claude.jsonand MCP settings, probes cloud metadata endpoints, and, per Wiz via The Hacker News, steals saved browser passwords. - On Linux GitHub Actions runners, reads the memory of the
Runner.Workerprocess to pull secrets out directly. Socket says this bypasses "all log masking applied by the CI platform."
Counts differ: Socket says 25 CI platforms and 80 plus file patterns, StepSecurity about 32 and about 134. On a developer machine it detaches so npm install looks normal. It exits on a Russian locale.
Exfiltration, spread and persistence
Stolen data is compressed, encrypted with AES-256-GCM and the key wrapped with an embedded RSA-4096 public key, so defenders cannot read what was taken. It is committed to new public repositories created on the victim's own GitHub account with the description "A Mini Shai-Hulud has Appeared." The Hacker News counted more than 1,100 such repositories at the time of its report.
It then tries to spread. StepSecurity says it publishes infected versions of packages the victim can write to, straight through the registry API, and injects a format-check.yml workflow that dumps toJSON(secrets) into an artifact on a branch named to look like Dependabot.
For persistence it commits two files into repositories it can reach, authored as claude@users.noreply.github.com with the message "chore: update dependencies":
.claude/settings.jsonwith aSessionStarthook, which runs when someone opens the project in Claude Code..vscode/tasks.jsonwith a task set to run on folder open in VS Code.
Each one launches the payload copy stored next to the other, so removing one file is not enough. StepSecurity called this "one of the first supply chain attacks to target AI coding agent configurations" as a persistence vector.
How to check
Work through these in order. Run the checks from a machine you trust, not the one you suspect.
-
Search the dependency tree in each project.
npm lsshows transitive copies too.npm ls mbt @cap-js/sqlite @cap-js/postgres @cap-js/db-service @cap-js/openapi --allAny of the versions in the table above means that project resolved a bad release at some point.
-
Search lockfiles across every branch, not only
main. Tarball URLs are the most reliable string to grep for inpackage-lock.jsonandyarn.lock; pnpm recordsname@version.PATTERN='mbt-1\.2\.48\.tgz|sqlite-2\.2\.2\.tgz|postgres-2\.2\.2\.tgz|db-service-2\.10\.1\.tgz|openapi-1\.4\.1\.tgz|mbt@1\.2\.48|@cap-js/(sqlite|postgres)@2\.2\.2|@cap-js/db-service@2\.10\.1|@cap-js/openapi@1\.4\.1' for b in $(git for-each-ref --format='%(refname)' refs/heads refs/remotes); do git grep -nE "$PATTERN" "$b" -- '*package-lock.json' '*yarn.lock' '*pnpm-lock.yaml' 2>/dev/null doneA hit in a
@cap-jsscoped URL (/@cap-js/sqlite/-/sqlite-2.2.2.tgz) is what you care about; ignore unrelated packages with the same file name pattern. -
Check the npm cache on developer machines. A cache entry proves the tarball was downloaded on that machine, even if the project was later fixed.
grep -rlE 'mbt-1\.2\.48\.tgz|@cap-js/(sqlite|postgres)/-/(sqlite|postgres)-2\.2\.2\.tgz|db-service-2\.10\.1\.tgz' ~/.npm/_cacache/index-v5 2>/dev/null | head -
Check CI history for the window. For GitHub Actions, list runs on 29 April (and 19 May if you use
@cap-js/openapi) and search their logs.gh run list --created 2026-04-29 --limit 500 --json databaseId,workflowName,createdAt gh run view <run-id> --log | grep -nE 'mbt@1\.2\.48|setup\.mjs|bun-v1\.3\.13|execution\.js'If logs have expired, check which jobs ran
npm installwithout a lockfile that day, and any caches or images built then. -
Look for files on disk. In projects and CI caches:
find . -path '*/node_modules/*' -name setup.mjs -maxdepth 5 2>/dev/null shasum -a 256 $(find . -path '*/node_modules/*' -name setup.mjs -maxdepth 5 2>/dev/null) ls -la .claude/settings.json .claude/setup.mjs .claude/execution.js .vscode/tasks.json .vscode/setup.mjs 2>/dev/null ps aux | grep -i '[b]un'StepSecurity and Onapsis publish the loader hash
4066781fa830224c8bbcc3aa005a396657f9c8f9016f9a64ad44a9d7f5f45e34. -
Look for the GitHub side effects. For each user and organisation:
gh repo list <account> --visibility public --limit 500 --json name,description,createdAt \ | grep -i 'shai-hulud' git log --all --author='claude@users.noreply.github.com' --oneline git log --all --grep='OhNoWhatsGoingOnWithGitHub' --oneline git branch -a | grep -i dependaboutAlso search workflow files for
toJSON(secrets)and check your npm packages' publish history for versions you did not release. -
Check cloud audit logs around the time of any install you find. Onapsis suggests looking for calls such as
GetSecretValuefrom developer IPs or CI runners shortly after an install.
If steps 1 to 4 are clean everywhere, including old branches, caches and images, you are very likely unaffected.
What to rotate, and in what order
-
Isolate the machine or runner. Disconnect it, then work from a clean machine. Do not revoke tokens from the infected one.
-
Revoke npm publish tokens first. They are how the worm spreads to packages you maintain. Then check whether any of your packages got new versions you did not publish, and deprecate or unpublish them.
-
Revoke GitHub tokens: personal access tokens, OAuth tokens behind
gh, deploy keys and any GitHub App credentials on the host. Checkgh auth statusfrom the clean machine afterwards. -
Rotate CI secrets for every workflow that ran an install on an affected runner. On GitHub hosted Linux runners, assume every secret available to that job was read from memory, whether or not it was printed.
-
Rotate cloud credentials: AWS, Azure and GCP keys, and the secrets in Secrets Manager, Key Vault and Secret Manager that those identities could read. Rotating the access key alone leaves the stored secrets exposed.
-
Rotate the rest: SSH keys, Kubernetes service account tokens and kubeconfigs, Docker registry credentials,
.envvalues, AI provider and MCP server keys, and passwords saved in browsers on that machine. -
Clean the repositories. Remove
.claude/settings.jsonhooks and.vscode/tasks.jsontasks you did not write, thesetup.mjsandexecution.jscopies, the injected workflow and branch. Delete the public dead drop repositories on your accounts. Check every branch, since StepSecurity says it commits to up to 50 per repository. -
Reinstall cleanly. Pin the fixed versions, delete
node_modules, clean the cache withnpm cache clean --force, regenerate the lockfile with scripts off, and review what changed:npm install mbt@1.2.49 @cap-js/sqlite@^2.4.0 @cap-js/db-service@^2.11.0 @cap-js/postgres@^2.3.0 --ignore-scripts npm ls mbt @cap-js/sqlite @cap-js/db-service @cap-js/postgres --all git diff package-lock.jsonIf a package you trust needs its install script, run
npm rebuild <package>for that one after review. Adjust the major versions to what your CAP release supports;@cap-js/sqliteis on 3.x now. -
Rebuild and redeploy images and MTA archives built from an affected tree, and purge CI caches that hold the old
node_modules.
Our incident response playbook for a compromised dependency covers the communication and evidence side.
What would have helped
Practices any team can use
The tensorlake write up covered why provenance alone did not stop a similar worm. This one adds its own lessons.
For anyone who publishes packages:
- No long lived publish tokens. The
mbtroute relied on a static token. Use OIDC trusted publishing so there is no token to steal. - Scope trusted publishing narrowly. The
@cap-jsroute went through trusted publishing configured for the whole repository. Bind it to one workflow file, and put publishing in a protected GitHub environment with required reviewers, which is what SAP's PR #1592 does. - Pin actions to commit SHAs and protect release branches, so a pushed branch cannot rewrite the release job.
- Fail the release if a lifecycle hook appears in a manifest that did not have one before.
For anyone who installs packages:
- Turn off dependency install scripts by default and keep a reviewed allowlist. Our install script policy guide has the exact settings for npm, pnpm, Yarn and Bun.
- Use a release cooldown. Every bad version here was replaced within hours. A one day minimum release age would have skipped all of them.
- Commit lockfiles and install with
npm ciin CI, so a caret range cannot resolve to a new release mid build. Pin exact versions for build tools such asmbt. - Separate install from secrets. Run dependency installation in a job that holds no deploy credentials, and pass artifacts to later jobs that do. Memory scraping on the runner makes "masked" secrets no protection.
- Prefer short lived cloud credentials through OIDC in CI, and restrict runner egress where you can.
- Review agent and editor config like code. Add
.claude/,.vscode/and.github/workflows/to CODEOWNERS so hooks and tasks need a human approval, and keep VS Code workspace trust on. - Know what you run. A current inventory turns "are we affected?" into a query. See SBOM incident response.
Where Safeguard fits
Safeguard would not have detected or prevented this. It does not scan packages for malicious code, and it does not flag new install scripts or maintainer changes. A malicious release with no advisory looks to it like any other version. Its part is the inventory and the response.
Finding every affected project. safeguard scan --dir . builds an SBOM for a repository, safeguard scan --image <name> does the same for a container image, and safeguard scan --sbom <file> ingests an existing SBOM; results land as JSON under ./output. Once projects are in the platform, it shows which projects contain a given component and version, so "who has @cap-js/db-service 2.10.1, including transitively" is a lookup. Scanning images matters because a container built in April can still hold the old node_modules.
Matching against advisories. The @cap-js versions now carry CVE-2026-46421, so vulnerability matching against databases that include it flags them in later scans. We did not find a CVE for mbt@1.2.48, so search for it by name and version.
Keeping them out. Platform policies and gates evaluate what a build contains, which helps stop an old lockfile from bringing a known bad version back.
Doing the fix with approval. The Safeguard CLI's assistant can edit code and run commands on your machine. In the default flow it asks before each one, and /permissions offers Ask, Accept edits, Plan, Auto and Strict modes. You can ask it to bump these packages, regenerate the lockfile with --ignore-scripts, find and remove unexpected .claude/settings.json hooks and .vscode/tasks.json tasks, and run your tests. The desktop and phone apps can send that work to a computer running the CLI and approve each command. In CI, safeguard exec runs the checks. Rotation at npm, GitHub and your cloud provider is still yours to do.
Zero-Day Discovery and Gold
These address a different problem, and it is worth being exact about it.
- Zero-Day Discovery looks for previously unknown vulnerabilities before a CVE exists, in an agentic mode and a deterministic scanner mode, across seven package ecosystems. Findings are issued as SGZ advisories before they reach public CVE databases. It hunts for flaws in code, not for deliberately planted malware, and it did not flag these releases.
- Gold Open Source at gold.safeguard.sh is free with no login: searchable CVE intelligence with CVSS, EPSS, KEV status, affected and fixed versions and a recommended safe version, plus a free read API and a CI gate GitHub Action.
- Gold Registry at registry.safeguard.sh is paid: hardened drop in components and container images held to zero exploitable critical or high CVEs, built to SLSA L3+, signed with Sigstore, with evidence pages. An artifact loses its badge if it regresses.
The habit that ties these together: pull dependencies from a curated, signed source, know exactly what you run, and be able to rebuild fast. For the wider picture of the month, see October 2026 security incidents and what would have helped.
Common mistakes
- Upgrading and stopping there. The payload ran at install time. A clean lockfile today does nothing about tokens stolen in April.
- Only checking direct dependencies.
@cap-js/db-servicecame in through@cap-js/sqlite. Usenpm ls --all. - Trusting masked CI logs. Masking hides values in output. It does not stop a process reading runner memory.
- Revoking from the infected machine. Work from a clean host, and revoke npm tokens before anything else.
- Deleting one persistence file.
.claude/settings.jsonand.vscode/tasks.jsoneach restart the payload from the other location. Remove both and their payload copies. - Reinstalling with scripts on. Regenerate the lockfile with
--ignore-scriptsand review before anything runs.
FAQ
Which versions were malicious?
mbt@1.2.48, @cap-js/sqlite@2.2.2, @cap-js/postgres@2.2.2 and @cap-js/db-service@2.10.1, all published on 29 April 2026. A separate compromised release, @cap-js/openapi@1.4.1, followed on 19 May. Versions before and after those are not listed as affected in the advisories.
Which versions should I move to?
The cds-dbs advisory lists @cap-js/sqlite 2.4.0 or later, @cap-js/postgres 2.3.0 or later and @cap-js/db-service 2.11.0 or later. For mbt, 1.2.49 is the current latest. Use @cap-js/openapi 1.4.2 or later. Some reports list different "safe" numbers, so confirm against the advisory and the registry.
Is mbt 1.2.49 safe?
The maintainers say 1.2.49 is "not infected," released manually and functionally identical to 1.2.47. One user reported a Microsoft Defender detection of it, unconfirmed in the thread. If in doubt, compare its tarball with 1.2.47.
I only ran npm install once that day. Is that enough to be affected?
Yes, if it resolved a bad version. The hook runs during install, before your code, and Onapsis notes credentials may be taken even if the install fails. Check the lockfile and cache from that install.
Does using a lockfile protect me?
A lockfile pinned to a clean version and installed with npm ci would have kept the clean version. A lockfile that was regenerated or updated during the window, or an npm install that allowed range updates, would not.
Why does this attack touch Claude Code and VS Code settings?
To run again later. It commits a SessionStart hook in .claude/settings.json and a folder open task in .vscode/tasks.json, so anyone who opens the repository in those tools can restart the payload. Review changes to those files the way you review CI workflows.
Can Safeguard tell me if I installed one of these?
It can tell you where these package versions appear in the repositories, images and SBOMs you have scanned, which is the inventory half. It cannot tell you whether malicious code ran on a given machine or whether a secret was stolen. Use the CI log, cache and GitHub checks above for that.
Fix it without waiting for upstream
In incidents like this one, most of the risk sits in two gaps. The first is the time between a fix being published and that fix actually running in your production builds. The second, and worse, is the time before any fix exists at all, while a maintainer works out a patch and a release schedule. Waiting through both gaps is a habit, not a requirement. Safeguard gives teams three ways to close them.
1. Autonomous remediation: pull requests, not tickets
When a fixed version exists, Safeguard's Griffin models open a tested, policy gated pull request that moves the affected dependency to the safe version. You can run it on one repository or in bulk across every project that carries the vulnerable version. Remediation covers eight categories, including dependencies, licenses, security posture and findings. Reachability analysis ranks the vulnerable code paths your application actually calls first, so the first pull requests you merge are the ones that matter most. Autonomous remediation ships switched off, with every category set to Manual, so your team decides how much runs on its own and what still needs a human approval.
2. Gold Open Source: the safe version in seconds, free
gold.safeguard.sh is free and needs no login. Search any CVE or package to see affected and fixed versions, CVSS, EPSS, CISA KEV status and a recommended safe version, or use the "Am I affected?" checker. The same data is available through a free read API, README status badges and RSS threat feeds. The safeguard-sh/gold-scan-action GitHub Action can fail a build on a known exploited vulnerability, on any known vulnerability, or never, so a version you have just removed cannot quietly come back in next week's merge.
3. Gold Registry: start from components that are already fixed
registry.safeguard.sh provides hardened, drop in replacements for upstream packages and container images. Every Gold artifact is held continuously to zero exploitable critical or high CVEs, zero malware and a completed license check, with SLSA L3 or higher provenance, a Sigstore signature and a public evidence page you can hand to an auditor. An artifact loses its Gold badge if it regresses. Teams can point their own package directory at the registry so hardened equivalents resolve without changing how developers install anything.
When upstream has no fix yet, or will not ship one, Safeguard first works to get the fix accepted upstream. If the issue is severe or upstream will not move, Safeguard builds a fork, fixes the vulnerability there and publishes a hardened Gold build, so you are not stuck on a maintainer's release schedule. You can also request a hardened, compatibility tested build of a specific package. This fork and patch flow is in early access today, so ask which packages it covers before you plan around it.
| Your situation | What to use |
|---|---|
| A fixed version exists | Autonomous remediation opens the upgrade pull request; Gold Open Source confirms the safe version |
| You need to know today whether you are affected | Gold Open Source lookup and "Am I affected?" checker, free and without login |
| You want vulnerable versions to stop shipping | The Gold scan GitHub Action in CI, plus Safeguard policies and gates |
| Upstream has no fix yet, or will not ship one | A custom Gold request; fork and patch (early access) |
| You want to start clean on new projects | Pull hardened components and images from Gold Registry |
One honest limit: when the vulnerable code lives in a vendor appliance, a hosted service or an operating system runtime you do not build yourself, the fix still has to come from that vendor or distribution. The steps above apply to the open source dependencies and container images you build and ship, which is where most teams carry the largest and least tracked share of their exposure.
See how Safeguard would have helped
None of these demos show this incident. Each is a real Safeguard session; the text says how the same flow applies here.
The first demo is a CLI session that fixes a lodash CVE: the assistant reads the finding and advisory, asks before each command, edits package.json, runs the install and tests with your approval, and pushes nothing. For this incident, you would open the same session in a CAP project and ask it to move @cap-js/sqlite to 2.4.0 or later, regenerate the lockfile with --ignore-scripts, run npm ls --all to prove db-service 2.10.1 is gone, and run your tests. It asks before each command.
Click through it at your own pace, or open it full screen.
The second demo is a desktop app session. The CLI session shows in the conversation list, you ask for work on your computer, pick the folder, then Approve or Deny each command and file edit, and it ends with an upgrade done and tests passing. Here you would pick a repository, ask it to find .claude/settings.json hooks and .vscode/tasks.json tasks that run setup.mjs, and approve each removal while you rotate tokens elsewhere.
Click through it at your own pace, or open it full screen.
The third demo is a real Android session: work is sent to a computer running the CLI, and the command is approved on the phone with Allow once, Allow for turn or Deny. During a cleanup across many repositories, that lets the on call engineer approve each lockfile regeneration from a phone, and deny anything that would run install scripts.
Click through it at your own pace, or open it full screen.
To try it on your own projects, download Safeguard and follow the CLI documentation.
Quick check
Your package.json lists only @cap-js/sqlite, never @cap-js/db-service. Can you skip checking db-service?
No. @cap-js/sqlite@2.2.2 declares @cap-js/db-service@^2.10.0, so an install during the window could pull the malicious 2.10.1 transitively. Run npm ls @cap-js/db-service --all and check the lockfile.
A CI job installed mbt@1.2.48, but its logs show every secret as masked. Are the secrets safe?
No. On Linux GitHub Actions runners the payload read the runner process memory, which Socket says bypasses log masking. Rotate every secret that job could access.
Sources
- GitHub Advisory Database: GHSA-pvw4-cvr4-97p8 (CVE-2026-46421)
- cap-js/cds-dbs security advisory GHSA-pvw4-cvr4-97p8
- cap-js/cds-dbs PR #1592: gate npm publish behind manual approval
- SAP/cloud-mta-build-tool issue #1224
- GitHub Advisory Database: GHSA-jpvj-wpmj-h7rv (@cap-js/openapi)
- npm registry metadata: mbt
- npm registry metadata: @cap-js/sqlite
- StepSecurity: A Mini Shai-Hulud has Appeared
- Socket: SAP CAP npm packages supply chain attack
- Snyk: Bun based stealer hits SAP CAP JS and mbt npm packages
- Onapsis: SAP CAP Mini Shai-Hulud supply chain attack
- Semgrep: SAP npm packages compromised in supply chain attack
- The Hacker News: SAP-related npm packages compromised
- BleepingComputer: Official SAP npm packages compromised to steal credentials
Last reviewed: 22 October 2026. This is a developing story; check the linked advisories for the latest status.