Six prerelease versions of @joyfill/components and @joyfill/layouts were published with an obfuscated remote-access trojan and credential stealer. The malicious code runs when an application imports the package—not during npm install—so npm install --ignore-scripts does not prevent execution. Developers should check lockfiles now; if an affected version was imported on a workstation or CI runner, isolate that host and treat its available credentials as exposed.
StepSecurity reported the compromise on July 28, 2026 after sandboxing all six builds, comparing their published tarballs with clean siblings, and extracting the injected loader. The code appeared in the built distribution files but not in matching project source changes. That supports a published-package compromise, not a vulnerability in every Joyfill release.
Which Joyfill versions are malicious?
| Package | Compromised prereleases |
|---|---|
@joyfill/components |
4.0.0-rc24-2773-beta.44.0.0-rc24-2773-beta.54.0.0-rc24-2773-beta.6 |
@joyfill/layouts |
0.1.2-2773.beta.00.1.2-2773.beta.10.1.2-2773.beta.2 |
The npm registry was already changing during verification on July 29. All three affected components builds and [email protected] were absent from the current versions object, while two affected layouts builds remained listed. Current removal is helpful, but it does not clean private caches, existing lockfiles, developer machines, CI runners, or artifacts built earlier.
A lockfile match proves package exposure, not that every malware stage completed. Installation alone did not trigger the sample in StepSecurity’s tests. Importing it through a unit test, bundler, development server, build, or production runtime is the important execution boundary.
Why ignore-scripts is not enough
Typical malicious npm packages use a preinstall or postinstall hook. These Joyfill builds placed the loader inside entry bundles such as dist/index.js, dist/index.esm.js, and dist/index.cjs.js. An install with scripts disabled can therefore look quiet, while the first application import starts the loader later.
The analyzed chain decoded its next stage in memory, used public blockchain transactions to resolve changing command infrastructure, and opened a Socket.IO remote-access channel. Reported commands covered host discovery, file and directory theft, clipboard access, uploads, downloads, and arbitrary JavaScript execution. A staged Python stealer targeted browser data, extension wallets and password managers, Git and GitHub CLI credentials, npm tokens, and operating-system keychains.
The trojan could also inject a reloading block into developer tools, including VS Code-family device modules, Discord Desktop, GitHub Desktop, and the global npm CLI. This is why deleting node_modules alone is not a complete recovery step after import-time execution.
How to check a repository and host
- Search every lockfile and build manifest. Look for
joyfilltogether with2773inpackage-lock.json,yarn.lock, andpnpm-lock.yaml. Include archived branches, CI caches, release workspaces, and private mirrors. - Establish whether the package was imported. Review test, build, development-server, and production logs. An install record without a later import is a different risk state from a runner or workstation that executed the bundle.
- Preserve evidence before cleanup. Save relevant process, network, file-integrity, CI, and identity logs. Look for unexpected Node or Python children, plain-HTTP Socket.IO traffic, blockchain API requests from Node, and modifications to developer application files.
- Check persistence targets. Reinstall affected developer applications if their files contain the reported marker comments, and verify the global npm CLI from a trusted package source.
- Review downstream output. Inspect builds, caches, containers, packages, and releases produced after an affected import. A clean dependency tree today does not prove an earlier artifact was built on a clean host.
Recovery after an affected version ran
- Isolate the workstation or runner from normal development and publishing workflows.
- From a clean environment, replace the packages with known-good releases published before July 28: StepSecurity used
@joyfill/[email protected]and@joyfill/[email protected]. - Delete
node_modules, restore a reviewed lockfile, clear untrusted dependency caches, and reinstall. - Reinstall or integrity-check any patched VS Code-family, Discord, GitHub Desktop, and npm CLI files.
- Rotate secrets available to the host: npm and GitHub tokens, cloud and CI credentials, SSH keys, application secrets, browser sessions, stored passwords, and wallet keys where applicable. Revoke old sessions before relying on new passwords.
- Review account, repository, registry, and cloud audit logs for actions performed after the first possible import.
On a Windows developer workstation, a security tool may remove a visible payload while a modified developer-app file or npm CLI keeps reloading it. After preserving evidence and disconnecting the host from sensitive workflows, use Gridinsoft Anti-Malware to check for dropped stealers and related persistence, then reinstall altered developer tools from trusted sources. A clean scan is one recovery signal; it does not replace credential rotation or artifact review.
If a token stealer ran here, logging back in can hand the attacker your new Discord session, email cookie, Steam token, or wallet access. Scan this Windows PC first, then reset passwords from a clean device.
Scan the affected Windows workstationFor broader context, see the AsyncAPI npm supply-chain incident, the IronWorm npm campaign, and the guide to software supply-chain attacks.
References
- Varun Sharma. “Compromised npm Packages: @joyfill/components and @joyfill/layouts Ship an Obfuscated Remote Access Trojan.” StepSecurity, July 28, 2026. primary technical analysis.
- Joyfill. “@joyfill/components.” npm Registry, accessed July 29, 2026. current package record.
- Joyfill. “@joyfill/layouts.” npm Registry, accessed July 29, 2026. current package record.

