Ringkasan Utama
Versi 0.5.144 SDK npm Tensorlake membawa hook preinstall yang menjalankan worm Shai-Hulud pencuri kredensial, dirilis lewat workflow proyek sendiri dengan provenance yang valid. Pertahanannya: blokir install script dependency secara default, pasang cooldown usia rilis, dan hapus persistence gh-token-monitor sebelum mencabut token GitHub apa pun.
Detail paling ganjil dari insiden ini adalah produknya. Tensorlake menjual platform cloud untuk menjalankan AI agent yang terisolasi dan kode buatan AI yang tidak tepercaya, dan SDK npm-nya justru menjalankan kode tidak tepercaya di mesin developer, di luar sandbox mana pun, begitu seseorang mengetik npm install.
Tulisan ini adalah pembacaan cermat atas tiga write-up primer: analisis payload dari SafeDep, laporan The Register, dan pull request revert dari Tensorlake sendiri, ditambah dokumentasi npm dan pnpm untuk kontrol yang saya sarankan. Saya tidak menjalankan payload dan tidak memulihkan binary apa pun, jadi bila sebuah klaim hanyalah inferensi SafeDep, saya sebutkan. Tidak ada exploit yang berfungsi di sini, hanya hal yang dibutuhkan defender.
Apa yang dirilis di tensorlake 0.5.144
Versi 0.5.144 package npm tensorlake berbahaya, dan 0.5.145 adalah perbaikannya. Sumber berbeda soal popularitas package ini: The Register menyebut sekitar 12.000 download per minggu, sedangkan SafeDep menyebut sekitar 106.000 per bulan. Itu jendela waktu yang berbeda, bukan kontradiksi sungguhan. Yang jelas ini SDK berukuran menengah, bukan nama yang dikenal semua orang.
Deteksinya cepat. SafeDep mengatakan npm mencatat publish pada 01:12:07 UTC dan ia menandai versi itu pada 01:20, delapan menit kemudian. The Register mengaitkan angka 11 menit dengan engine Socket. Saya tidak bisa membaca postingan Socket sendiri (fetch saya mendapat 403), jadi saya mengutip kedua angka apa adanya dan tidak mencoba menyamakannya.
Bagian yang seharusnya mengkhawatirkan maintainer adalah cara masuknya. SafeDep mencatat delapan commit lewat antarmuka web GitHub, semuanya berstatus verified, berakhir pada version bump. PR revert Tensorlake menyebut versi berbahaya itu di-commit langsung ke main dan dirilis lewat workflow publish_npm.yaml. Sumber-sumber itu tidak menjelaskan bagaimana akun administrator diambil alih, dan saya tidak akan menebak.
Jalur rilis adalah titik lemahnya
Temuan SafeDep: penyerang tidak butuh npm token. Akun administrator repositori menjalankan workflow publish proyek secara manual, dan rilis memakai npm trusted publishing. Package tensorlake-native bersaudara di versi 0.5.144 membawa provenance SLSA yang valid untuk run itu, jadi provenance-nya asli sementara source yang dideskripsikannya sudah teracuni.
Itu bukan cacat pada provenance. Dokumentasi npm menggambarkannya sebagai bukti kriptografis tentang di mana dan bagaimana package dibangun, dan menggambarkan trusted publishing sebagai penghapus token berumur panjang lewat kredensial OIDC berumur pendek yang spesifik untuk satu workflow. Keduanya bekerja sebagaimana mestinya. Tak satu pun mengklaim source tree-nya aman, padahal itulah yang diam-diam diasumsikan banyak tim ketika melihat badge provenance hijau.
PR revert Tensorlake menunjukkan apa yang diubah maintainer: me-revert semua commit setelah commit bersih terakhir, mengganti tiga workflow ke npm ci --ignore-scripts, menambah tripwire yang berjalan sebelum instalasi dependency, dan menaikkan versi ke 0.5.145. Pengaturan repositori yang diubah di luar PR mencakup branch protection dan reviewer pada environment npm. Menurut saya, reviewer kedua yang wajib pada environment rilis adalah kontrol yang akan mengubah satu akun admin yang dibajak menjadi job yang terblokir, tetapi itu inferensi saya, bukan klaim sumber.
Hook preinstall dan apa yang dicuri worm
Pintu masuknya adalah lifecycle script saat instalasi, yang dijalankan npm otomatis dengan izin siapa pun yang menjalankan install. Peringatan Socket, seperti dikutip The Register, adalah bahwa ini terjadi di mesin developer atau build server, di luar sandbox Tensorlake, sebelum kode buatan AI mana pun dijalankan.
// The shape of the hook, as SafeDep describes it. package.json in 0.5.144:
// "scripts": { "preinstall": "node lib/setup.mjs" }
//
// What lib/setup.mjs did, in order (SafeDep's analysis, not my reproduction):
// 1. exit early if CI, GITHUB_ACTIONS, GITLAB_CI or RUNNER_ENVIRONMENT is set
// 2. download Bun 1.3.13 from the official GitHub release
// 3. run lib/Math_Symbol.js (856,501 bytes) with that Bun
//
// Step 1 is why "our CI install was clean" proves nothing. The hook was
// written to skip the build server and run on the laptop.
//
// Check your own tree for the three files SafeDep names:
// lib/setup.mjs 32,645 bytes
// lib/Math_Symbol.js sha256 b50a00900399ba99fb6ce1fc151519cb99d44320ef2a631f2237e1aea0ad6fec
// math_init.js (written by the worm into repos it touches)Daftar target pengumpulan data dari SafeDep panjang. Saya kelompokkan berdasarkan apa yang harus Anda rotasi. Kolom kedua adalah deskripsi SafeDep, kolom ketiga adalah saran saya, bukan milik mereka.
| Target | Yang dibaca menurut SafeDep | Respons defender |
|---|---|---|
| File di disk | SSH key, config cloud CLI, .npmrc, .env, file Docker dan Kubernetes, ~/.claude.json, ~/.kiro/settings/mcp.json, shell history | Rotasi semua kredensial yang tersimpan di file itu, termasuk token MCP dan tool AI |
| Wallet kripto | wallet.dat, Exodus, Electrum, Ledger Live, ekstensi browser seperti MetaMask dan Phantom | Pindahkan dana ke wallet baru yang dibuat dari mesin yang bersih |
| Password browser | Binary yang diunduh dan dijalankan dengan argumen dump password; SafeDep menduga itu HackBrowserData tetapi tidak berhasil memulihkannya | Anggap semua login tersimpan di browser sudah terbuka, lalu ganti |
| AWS | STS, SSM Parameter Store, Secrets Manager | Rotasi key, lalu tinjau CloudTrail untuk akses ke layanan itu |
| GitHub Actions | Secret yang dibaca dari memori proses Runner.Worker | Rotasi secret repositori dan organisasi, tetapi hanya setelah token monitor dihapus |
| Kubernetes dan Vault | Token service account dan token Vault | Cabut dan terbitkan ulang, lalu periksa audit log untuk pemakaian dari alamat asing |
Command and control dirancang agar tahan takedown. SafeDep melaporkan endpoint utama, fallback yang membaca contract Ethereum lewat 35 endpoint RPC publik, fallback yang hanya memercayai commit GitHub bertanda tangan RSA key tertanam, dan repositori dead-drop terenkripsi dengan deskripsi 'Shai-Hulud: Here We Go Again'. Setelah exfiltration, ia melakukan polling tiap 45 sampai 90 detik dan bisa menjalankan kode yang dikirim server. Memblokir satu domain tidak mengakhiri insiden.
Token monitor yang menghukum pencabutan token
SafeDep dan The Register sama-sama menjelaskan perilaku paling jahat. Worm memasang watcher yang memeriksa token GitHub curian, dan jika token berhenti berfungsi, ia menghapus home directory pengguna yang terinfeksi. SafeDep merinci mekanismenya: watcher hanya aktif untuk token yang akunnya tidak punya organisasi, memeriksa tiap 60 detik selama 24 jam, dan pada respons 40x dari GitHub menjalankan penghapusan rekursif home directory.
SafeDep menyebut nama persistence-nya: systemd user service bernama gh-token-monitor.service di Linux, LaunchAgent berlabel com.user.gh-token-monitor di macOS, dan scheduled task bernama gh-token-monitor di Windows. The Register meneruskan saran peneliti untuk menonaktifkan monitor sebelum mencabut kredensial, dan SafeDep mengatakan hal yang sama dengan lugas.
Urutan lebih penting daripada kecepatan. Temukan dan hapus gh-token-monitor di setiap host terdampak sebelum Anda mencabut satu pun token GitHub. Mencabut lebih dulu bisa memicu penghapusan. Jika monitor tidak ditemukan, bekerjalah dari mesin yang dibangun ulang, seperti yang disarankan Socket dalam laporan The Register.
# Read-only discovery first. Nothing here revokes a token.
# Linux: the systemd user service and its files (names from SafeDep)
systemctl --user list-unit-files | grep gh-token-monitor
ls -la ~/.local/bin/gh-token-monitor.sh ~/.config/gh-token-monitor/ 2>/dev/null
# macOS: the LaunchAgent label
launchctl list | grep com.user.gh-token-monitor
# Only after you have found it, stop it. THEN rotate GitHub tokens.
systemctl --user disable --now gh-token-monitor.service # Linux
launchctl remove com.user.gh-token-monitor # macOS
# Windows (PowerShell): scheduled task named gh-token-monitor
# Get-ScheduledTask -TaskName gh-token-monitor
# Is the package, or its files, anywhere in this checkout?
grep -n '"tensorlake"' package-lock.json | head
find . -path ./node_modules/.cache -prune -o \
\( -name Math_Symbol.js -o -name math_init.js \) -print
grep -rl "WORMTAG" --include=*.js --include=*.mjs . 2>/dev/null | headPerintah di bawah hanya membaca sampai baris stop. Saya menulisnya dari nama artefak yang dipublikasikan SafeDep, dan belum menjalankannya pada infeksi nyata, jadi cocokkan nama dengan daftar indikator SafeDep sebelum mengandalkan hasil negatif.
Garis keturunan ChainDrop dan alasan scanner melewatkannya
The Register menyebut analisis mengaitkan rilis Tensorlake dengan ChainDrop, varian Shai-Hulud yang pertama terlihat pada 4 Agustus 2026. Kampanye itu menginfeksi 444 package dari beberapa publisher yang bersama-sama diunduh sekitar 2 miliar kali per bulan, termasuk keyv, flat-cache, dan cache-manager. SafeDep menambah kaitan konkret: payload Tensorlake memakai salt cipher yang sama dengan payload keyv.
Dua ciri penting bagi defender. Pertama, ChainDrop menyebar lewat tarball npm yang dibangun ulang, sehingga membaca source repositori bisa tidak menunjukkan apa-apa. Kedua, ia menanam startup hook di konfigurasi repositori, dan The Register menyebut sekadar membuka branch terinfeksi di VS Code atau Claude Code bisa mengaktifkannya. SafeDep melaporkan varian Tensorlake meng-commit hook Claude Code dan VS Code dengan author bernama claude serta menanam workflow berjudul 'Run Copilot' yang mengeluarkan secret repositori lalu menghapus run dan branch-nya.
# Look for repository config you did not write, on EVERY branch.
# Checking only main is the mistake the ChainDrop advice warns about.
git fetch --all --prune
for ref in $(git for-each-ref --format='%(refname)' refs/remotes); do
git ls-tree -r --name-only "$ref" \
| grep -E '^(\.claude/settings\.json|\.vscode/tasks\.json)$' \
| sed "s|^|$ref |"
done
# SafeDep also lists a planted branch name and a planted workflow title.
git branch -r | grep 'dependabot/github_actions/format/setup-formatter'
git grep -n "Run Copilot" -- .github/workflowsAbby Kearns dari ActiveState, yang dikutip dalam berita Agustus The Register, menyarankan memeriksa setiap branch untuk file .claude/settings.json dan .vscode/tasks.json yang tidak Anda buat, dan memperlakukan konfigurasi dari repositori sebagai konten yang dapat dieksekusi. Ia juga menyebutnya kampanye pertama yang menyadari celah pada dependency scanning dan memakainya dalam skala besar. SafeDep mencatat worm ini punya bug yang mungkin membatasi penyebarannya, misalnya jalur npm berhenti jika file yang diharapkan tidak ada. Saya tidak akan berharap bug itu bertahan.
Mengunci install script
Perbaikan termurah adalah yang diterapkan Tensorlake pada pipeline-nya sendiri: jangan biarkan dependency menjalankan kode saat instalasi kecuali Anda memilihnya. Tabel memetakan tiap kontrol ke perilaku yang terdokumentasi dan ke dampaknya pada hook ini. Kolom terakhir adalah penalaran saya, bukan hasil uji.
| Kontrol | Setting | Perilaku terdokumentasi | Efek pada insiden ini (penalaran saya) |
|---|---|---|---|
| npm: lewati lifecycle script | ignore-scripts=true | Jika true, npm tidak menjalankan script yang ditentukan di package.json. Default false. | Hook preinstall tidak pernah mulai, jadi tidak ada loader, unduhan Bun, atau payload |
| pnpm: allowlist build | allowBuilds, strictDepBuilds | Package yang tidak terdaftar dianggap unreviewed; strictDepBuilds, default true, keluar dengan kode non-nol bila ada build script yang belum ditinjau | Versi baru package yang tidak terdaftar menggagalkan install, bukan menjalankan hook-nya |
| Cooldown usia rilis | min-release-age, minimumReleaseAge | npm mendokumentasikan min-release-age dalam hari, default null. pnpm mendokumentasikan minimumReleaseAge dalam menit, default 1440 sejak v11 dan 0 sebelumnya | Hanya membantu jika versi buruk ditarik sebelum cooldown habis. Deteksi hanya butuh menit di sini, tetapi saya tidak menemukan waktu penarikannya |
| Install CI tanpa script | npm ci --ignore-scripts | Flag yang ditambahkan PR Tensorlake ke publish_npm.yaml, typescript_sdk.yaml, dan tests.yaml | Melindungi runner build dan rilis, yang memang dilewati loader; laptop tetap butuh setting .npmrc |
| Trusted publishing plus environment terproteksi | id-token: write, nama environment opsional | Dokumen npm: token OIDC berumur pendek; field environment opsional dan ditujukan untuk deployment protection | Tidak menghentikan instalasi. Bisa menaruh persetujuan manusia di antara akun admin yang dibajak dan sebuah rilis |
# ---- .npmrc (npm) ----
# Do not run any package's lifecycle scripts during install.
ignore-scripts=true
# Only resolve versions published more than N days ago. The npm CLI v11
# config page documents this key in days; run "npm config get min-release-age"
# on your own npm to confirm it is recognised before you rely on it.
min-release-age=3
# ---- pnpm-workspace.yaml (pnpm) ----
# Minutes. The pnpm docs give 1440 (one day) as the default since v11, 0 before.
minimumReleaseAge: 1440
# Build scripts are an allowlist. Unlisted packages are "unreviewed".
allowBuilds:
esbuild: true
# Default true: exit non-zero when a dependency has an unreviewed build script.
strictDepBuilds: truePR Tensorlake juga menambah tripwire yang menggagalkan CI jika manifest yang dipublikasikan mendeklarasikan install lifecycle script atau jika folder lib SDK berisi file tak terduga. Saya belum melihat script mereka, hanya deskripsinya. Sketsa di bawah adalah versi saya sendiri dari ide yang sama, memakai field package-lock.json v3, dan tidak butuh jaringan.
// scripts/check-no-install-scripts.mjs
// My own sketch of the tripwire idea from Tensorlake's revert PR (their script
// is described there, not reproduced here). Run it BEFORE any install.
// package-lock.json v3 marks every package that ships an install-time script
// with hasInstallScript: true, so this needs no network and no install.
import { readFileSync } from "node:fs";
// Packages you have reviewed and accept running build steps for.
const ALLOWED = new Set(["node_modules/esbuild"]);
const lock = JSON.parse(readFileSync("package-lock.json", "utf8"));
const offenders = Object.entries(lock.packages ?? {})
.filter(([path, meta]) => path !== "" && meta.hasInstallScript)
.map(([path]) => path)
.filter((path) => !ALLOWED.has(path));
// Your own manifest should not declare install-time hooks either: that is
// exactly where the 0.5.144 payload was attached.
const own = JSON.parse(readFileSync("package.json", "utf8")).scripts ?? {};
const ownHooks = ["preinstall", "install", "postinstall"].filter((h) => h in own);
if (offenders.length || ownHooks.length) {
console.error("Unreviewed install scripts:", offenders.concat(ownHooks));
process.exit(1);
}
console.log("No unreviewed install scripts.");# .github/workflows/ci.yml (fragment)
permissions:
contents: read # no id-token: write on jobs that only build and test
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: node scripts/check-no-install-scripts.mjs # before the install
- run: npm ci --ignore-scripts # as in Tensorlake's PR
- run: npm testKedua bagian saling melengkapi. Setting .npmrc melindungi laptop, tempat worm ini ingin berjalan. Fragmen CI dan tripwire melindungi pipeline dan memberi Anda check yang gagal pada hari seseorang menambah dependency dengan install script.
Perlakukan juga file tooling AI sebagai secret. SafeDep mencantumkan ~/.claude.json dan ~/.kiro/settings/mcp.json di antara file yang dibacanya. Jika token MCP server tersimpan di sana, masukkan ke daftar rotasi dan, lebih baik lagi, ke secret manager dengan masa berlaku pendek.
Yang rusak, dan yang tidak diselesaikan ini
Mematikan install script memang merusak beberapa hal. Package yang meng-compile kode native atau mengunduh binary saat instalasi, esbuild contoh umumnya, akan gagal atau terpasang tidak lengkap sampai Anda memasukkannya ke allowlist. Di pnpm itu daftar pendek yang ditinjau sekali; di npm biasa, ignore-scripts=true bersifat semua atau tidak sama sekali, jadi siapkan beberapa langkah build yang dijalankan eksplisit. Biayanya nyata, dan itu alasan banyak tim tidak pernah mengaktifkannya.
Cooldown menukar kesegaran dengan keamanan. Penundaan satu atau tiga hari juga menunda patch keamanan yang sah, jadi simpan daftar pengecualian untuk package yang perbaikannya Anda mau segera, dan jangan pernah memakai cooldown sebagai pengganti membaca advisory. Dokumen npm dan pnpm sama-sama menyediakan setting pengecualian untuk tujuan itu.
Kegagalan yang lebih dalam adalah yang dimanfaatkan loader: ia keluar saat berjalan di CI. Pipeline hijau tidak mengatakan apa-apa tentang laptop developer yang menyimpan SSH key, kredensial cloud, dan token tool AI. Dan tak satu pun setting ini membantu setelah akun admin maintainer diambil alih, karena rilis beracun itu dibangun secara sah. Itu butuh branch protection, reviewer wajib pada environment rilis, dan sedikit orang dengan hak admin.
Yang dikerjakan hari Senin
Kerjakan berurutan. Urutannya disengaja, karena langkah ketiga adalah yang bisa menghapus home directory.
- Cari tensorlake versi 0.5.144 di setiap lockfile dan mesin, lalu pindah ke 0.5.145 atau lebih baru; jika 0.5.144 pernah terpasang, anggap mesin itu terkompromi dan bangun ulang, jangan sekadar dibersihkan.
- Cari artefak yang didaftar SafeDep: lib/setup.mjs, lib/Math_Symbol.js, math_init.js, service, agent atau task gh-token-monitor, dan branch dependabot yang ditanam.
- Hentikan gh-token-monitor di setiap host terdampak sebelum mencabut token GitHub apa pun, lalu rotasi kredensial GitHub, npm, cloud, SSH, Kubernetes, dan Vault dari mesin yang bersih.
- Audit GitHub untuk .claude/settings.json dan .vscode/tasks.json di setiap branch, workflow berjudul Run Copilot, dan repositori publik asing di bawah akun Anda.
- Tambahkan ignore-scripts=true ke .npmrc atau allowlist pnpm, plus cooldown usia rilis dengan daftar pengecualian pendek, dan jalankan npm ci --ignore-scripts di CI.
- Pasang branch protection di default branch, wajibkan reviewer pada environment rilis npm, dan kurangi jumlah administrator repositori.
Jika Anda memelihara package sendiri, periksa juga package terpublikasi mana yang bisa dijangkau token Anda. SafeDep melaporkan worm menaikkan patch version setiap package yang bisa dipublish sebuah npm token, jadi blast radius satu token adalah semua package yang dapat dipublish-nya.
Aturan yang saya ambil: install script sebuah dependency adalah kode yang tidak Anda tinjau yang berjalan dengan izin Anda, jadi tolak secara default, izinkan sebagai pengecualian, dan jangan pernah menganggap CI hijau atau badge provenance valid sebagai bukti package itu aman.
Sumber dan bacaan lanjutan
- SafeDep: Mini Shai-Hulud di tensorlake 0.5.144, payload dan indikator
- The Register: worm Shai-Hulud melompat ke infrastruktur AI lewat kompromi Tensorlake
- Pull request Tensorlake 1016: revert kompromi 0.5.144 dan perkuat jalur rilis npm
- The Register: worm ChainDrop merayap ke supply chain npm
- Dokumen npm: config, ignore-scripts dan min-release-age
- Dokumen pnpm: minimumReleaseAge dan minimumReleaseAgeExclude
- Dokumen pnpm: allowBuilds dan strictDepBuilds
- Dokumen npm: trusted publishers, OIDC dan provenance

