Skip to content
osbytes
← back to blog

jscrambler 8.14.0 shipped a preinstall hook with a Rust infostealer and no matching git tag

2026-07-11by@osbytes8 min read
#npm #supply-chain #security #javascript #ci-cd #credentials #incident-response

TL;DR

  • StepSecurity's analysis (July 11) of jscrambler@8.14.0: tarball jumps from 37.8 kB (8.13.0) to 7.9 MB, adds a preinstall hook running dist/setup.js, which unpacks a Rust native binary from dist/intro.js (custom container, not JavaScript) into a random dotfile under the OS temp directory and spawns it detached.
  • No matching commit, tag, or release for 8.14.0 in the public jscrambler GitHub repository per StepSecurity and SafeDep; latest tag remains 8.13.0. The version went straight to npm under the legitimate maintainer account.
  • Static analysis points to a browser-credential and crypto-wallet stealer (Chrome/Firefox Login Data, LevelDB for extension wallets, embedded BIP39 wordlist). The Linux binary imports libbpf and loads eBPF via bpf_object__open_mem from an in-memory buffer.
  • Remediation: if you installed 8.14.0, treat the host as compromised; downgrade to 8.13.0, hunt orphaned temp-dir processes from install time, rotate every browser-session credential and CI secret reachable from affected machines. 8.14.0 was not unpublished; lockfiles can still resolve it.

What jscrambler is and why this hurts

jscrambler is the official npm CLI for Jscrambler's commercial JavaScript obfuscation and code-integrity service. It has a long version history, multiple @jscrambler.com maintainers, and real presence in CI pipelines that obfuscate production bundles before deploy. That makes a trojanized release a high-value supply-chain hit, not a obscure typosquat.

StepSecurity's OSS AI Package Analyst scored 8.14.0 at 0 (maximum suspicion) on publish the same day, triggering their manual teardown.

How the install hook works

Version 8.14.0's package.json adds a preinstall script absent from prior releases. dist/setup.js reads dist/intro.js, checks a 5-byte magic header (1B 43 53 49 01), and parses a custom container: per-platform ID byte, size fields, gzip-compressed native executables for Linux, Windows, and macOS. The loader picks the entry matching process.platform, decompresses to a randomly named dotfile in the system temp directory (.exe suffix on Windows), chmods executable, and spawns detached with unref() so the process survives after npm install exits.

Hiding compiled payloads inside a .js filename next to legitimate build artifacts like mutations.js keeps the loader script short; the malicious logic never appears as reviewable JavaScript source.

Payload capabilities beyond a typical npm stealer

StepSecurity's static analysis of the extracted binaries surfaced:

  • Embedded SQLite strings matching Chrome/Firefox credential stores (Login Data, Cookies, Web Data, key4.db).
  • Embedded LevelDB (Chromium Local Storage / IndexedDB, where MetaMask-style extension wallets persist vault data).
  • Embedded BIP39 English wordlist for parsing wallet seed phrases.

On Linux, the binary dynamically links libbpf.so.1 and imports bpf_object__open_mem, bpf_object__load, bpf_program__attach, and related symbols. bpf_object__open_mem loads eBPF bytecode from an in-memory buffer embedded in the binary, so the program never hits disk before kernel load. StepSecurity notes recovering what that eBPF program does requires extracting and disassembling the bytecode (ongoing work). The binary also imports the generic syscall function instead of named wrappers for sensitive calls, keeping ptrace / memfd_create out of the import table.

Windows imports include IsDebuggerPresent and GetExtendedTcpTable (enumerate TCP connections by PID, often used to fingerprint analysis environments or close browser processes before copying credential stores). macOS imports sysctl / sysctlbyname for P_TRACED debugger detection.

Harden-Runner network captures from StepSecurity customers show outbound calls to check.torproject.org, archive.torproject.org, and IPs 37.27.122.124 and 57.128.246.79.

Publish path anomaly

StepSecurity and SafeDep both report no commit, tag, or pull request for 8.14.0 in the public jscrambler GitHub repository; the latest tag remains 8.13.0 (published June 30). The malicious version appeared on npm under the legitimate jscrambler_ maintainer account without the project's normal git-tagged release flow. That pattern points to compromised npm credentials or a hijacked publish pipeline; which one is not established in the public write-up.

8.15.0 landed the same day from the same account without the preinstall hook or bundled binary, but 8.14.0 remains installable from the registry. Any lockfile or cache that pinned 8.14.0 can still pull the bad tarball.

If you installed 8.14.0

Assume compromise on any machine or CI runner that ran npm install resolving to jscrambler@8.14.0:

  1. Downgrade or pin clean: npm install jscrambler@8.13.0 and purge 8.14.0 from lockfiles and caches.
  2. Hunt persistence: random dotfiles in /tmp, %TEMP%, or $TMPDIR created around install time; orphaned child processes spawned during npm install; Windows Task Scheduler hidden tasks; macOS ~/Library/LaunchAgents plists you do not recognize.
  3. Rotate credentials logged into browsers on affected hosts: cloud consoles, npm/GitHub tokens, SSO sessions, extension-based crypto wallets. Move assets from wallets whose extension storage may have been read to fresh seeds on clean hardware.
  4. CI pipelines: if jscrambler ran in a build workflow, rotate every secret injected into that job (npm tokens, cloud keys, code-signing material).
IOC Value
Malicious package jscrambler@8.14.0
Suspicious file node_modules/jscrambler/dist/intro.js (~7.8 MB, magic 1B 43 53 49 01)
Linux payload SHA256 fbbcf4d8f98168f78f5c0c47a9ae56d59ec8ac84a7c9ca6b797fedfb8d62d2bd
Windows payload SHA256 b7ca95d1b23c8e67416a25cedf741de0917c2096bbc9d24649eea7853d054903
macOS payload SHA256 c8fd47d36bdf7c825378593ab82ed8c24d1dc52e26b507812393e24e1d5201fd

Why this is not "just another npm incident"

The install-time execution pattern matches recent scope compromises (Mastra's easy-day-js caret trap, TanStack, and others). What stands out here is the official vendor CLI with a decade-long clean history, a git-less npm publish, a native container disguised as JavaScript, and kernel instrumentation capability on Linux from a package many teams treat as build hygiene.

If your registry policy still keys only on postinstall scripts or known malware package names, this release is a reminder that preinstall runs before your audit step finishes and that absence from git history is a signal worth blocking on, not a footnote.

Sources