Dependency adalah package buatan orang lain yang dipasang aplikasimu, lewat npm di JavaScript atau Composer di PHP. Kalau ada celah keamanan di salah satunya, aplikasimu ikut kena. Bayangin restoran yang memakai saus dari pemasok. Suatu hari pemasok mengumumkan penarikan: "batch 1.0.0 sampai 1.2.5 tercemar, ganti dengan batch 1.2.6". Restoran mencocokkan pengumuman itu dengan catatan stok di gudangnya, lalu menukar yang kena. Pengumumannya disebut advisory, catatan stoknya lockfile, dan petugas yang mencocokkan adalah npm audit.
🧪 Tebakan cepat:
package.jsonmenulis"minimist": "^1.2.5", versi terpasang 1.2.5, dan perbaikannya ada di 1.2.6. Bisa gaknpm audit fixmemasang perbaikannya tanpa mengubahpackage.json? Tebak dulu, jawabannya ada di bagian "Range di package.json menentukan perbaikannya".
Advisory, lockfile, dan npm audit
- Advisory adalah pengumuman publik: versi berapa sampai berapa dari sebuah package punya celah, seberapa berbahaya (severity: low, moderate, high, critical), dan versi mana yang sudah diperbaiki. Tiap advisory punya ID, misalnya
GHSA-xvch-5gv4-984hdi GitHub Advisory Database. - Lockfile (
package-lock.json,composer.lock) mencatat versi persis setiap package yang terpasang, termasuk dependency dari dependency, plus hashintegrity-nya. npm auditmencocokkan lockfile dengan database advisory.
Ini proyek tokokopi yang sama dengan di Lab. Isi package.json-nya "dayjs": "^1.11.13", "jsonwebtoken": "^8.5.1", dan "minimist": "^1.2.5", dan lockfile-nya mencatat 1.11.13, 8.5.1, dan 1.2.5. Output npm audit aslinya:
$ npm audit
# npm audit report
jsonwebtoken <=8.5.1
Severity: high
jsonwebtoken unrestricted key type could lead to legacy keys usage - https://github.com/advisories/GHSA-8cf7-32gw-wr33
jsonwebtoken's insecure implementation of key retrieval function could lead to Forgeable Public/Private Tokens from RSA to HMAC - https://github.com/advisories/GHSA-hjrf-2m68-5959
jsonwebtoken vulnerable to signature validation bypass due to insecure default algorithm in jwt.verify() - https://github.com/advisories/GHSA-qwph-4952-7xr6
fix available via `npm audit fix --force`
Will install [email protected], which is a breaking change
node_modules/jsonwebtoken
minimist 1.0.0 - 1.2.5
Severity: critical
Prototype Pollution in minimist - https://github.com/advisories/GHSA-xvch-5gv4-984h
fix available via `npm audit fix`
node_modules/minimist
2 vulnerabilities (1 high, 1 critical)
minimist 1.0.0 - 1.2.5: nama package dan range versi yang rentan. Tanda minus di sini artinya "sampai", dan kedua ujungnya ikut rentan.Severity: critical: tingkat bahaya tertinggi dari advisory package itu.- Satu baris per advisory: judulnya dan link ke detailnya.
fix available via ...: perintah yang memasang perbaikannya. Bedanya dibahas dua bagian lagi.dayjsgak muncul: versinya gak masuk advisory mana pun.- Kalau ada temuan,
npm auditkeluar dengan exit code 1, jadi langkah CI yang menjalankannya gagal. Kalau bersih, dia mencetakfound 0 vulnerabilitiesdengan exit code 0.
Rentan atau gak: bandingkan versi sebagai angka
Versi ditulis semver: MAJOR.MINOR.PATCH. Patch naik untuk perbaikan bug, minor untuk fitur baru yang gak merusak, major untuk perubahan yang bisa merusak kode lama (breaking change). Untuk tahu apakah sebuah versi masuk range rentan, bandingkan tiap bagian sebagai angka:
function checkVulnerableVersion(version, vulnerableRange) {
const toNum = (v) => {
const [major, minor, patch] = v.split(".").map(Number);
return major * 1000000 + minor * 1000 + patch;
};
const ver = toNum(version);
return ver >= toNum(vulnerableRange.min) && ver <= toNum(vulnerableRange.max);
}
console.log(checkVulnerableVersion("1.2.5", { min: "1.0.0", max: "1.2.5" })); // true
console.log(checkVulnerableVersion("1.2.6", { min: "1.0.0", max: "1.2.5" })); // false
v.split(".")memecah"1.2.5"jadi["1", "2", "5"], dan.map(Number)mengubahnya jadi angka[1, 2, 5].toNummenggabungkan tiga angka jadi satu yang bisa dibandingkan: 1.2.5 jadi 1.002.005. Cara ini benar selama tiap bagian di bawah 1.000.>=dan<=membuatmindanmaxsendiri ikut dihitung rentan.
version |
toNum |
Masuk 1.0.0 sampai 1.2.5? |
|---|---|---|
| 0.9.9 | 9.009 | tidak, di bawah min |
| 1.0.0 | 1.000.000 | ya, tepat di min |
| 1.2.5 | 1.002.005 | ya, tepat di max |
| 1.2.6 | 1.002.006 | tidak, di atas max |
Range di package.json menentukan perbaikannya
package.json gak menulis satu versi, tapi range yang boleh dipasang:
^1.2.5(caret): dari 1.2.5 sampai sebelum 2.0.0. Major tetap, minor dan patch boleh naik.~1.2.5(tilde): dari 1.2.5 sampai sebelum 1.3.0. Cuma patch yang boleh naik.1.2.5: persis versi itu.
npm audit fix cuma memasang perbaikan yang masih masuk range. Perbaikan minimist ada di 1.2.6, dan 1.2.6 masuk ^1.2.5. Itu jawaban tebakannya: bisa, tanpa menyentuh package.json.
jsonwebtoken beda. Range-nya ^8.5.1, padahal gak ada versi 8.x yang aman: perbaikannya mulai 9.0.0, major baru. Karena itu npm menulis npm audit fix --force dan "which is a breaking change". --force mengubah package.json ke major baru. Sebelum merge, baca changelog-nya (jsonwebtoken 9 mengubah beberapa default) dan jalankan test.
Composer, Dependabot, dan CI
composer auditmelakukan hal yang sama untukcomposer.lockdi proyek PHP.- Dependabot adalah fitur GitHub yang memantau dependency, memberi alert saat ada advisory baru, dan membuka pull request berisi versi perbaikannya.
- Di CI, pasang dengan
npm ci(memasang persis isi lockfile, gagal kalau lockfile danpackage.jsongak cocok), lalu jalankannpm audit --audit-level=highsupaya build gagal kalau ada temuan high atau critical.
⚠️ Jebakan yang sering ketemu
- Membandingkan versi sebagai teks.
"1.10.0" > "1.9.0"hasilnyafalse, karena teks dibandingkan huruf per huruf dan "1" lebih kecil dari "9". Pecah dengansplit(".")dan bandingkan angkanya. - Lupa ujung range.
mindanmaxsendiri ikut rentan: pakai>=dan<=, bukan>dan<. - Menjalankan
npm audit fix --forcetanpa test. Dia bisa menaikkan major, dan kode lamamu bisa rusak. - Mem-pin versi persis tanpa
^. Dengan"minimist": "1.2.5",npm audit fixgak bisa apa-apa: npm menulis "Will install [email protected], which is outside the stated dependency range".
TL;DR: Advisory menyebut range versi yang rentan, lockfile mencatat versi persis yang terpasang, dan npm audit (atau composer audit) mencocokkan keduanya. Sebuah versi rentan kalau min <= version <= max, dibandingkan per bagian sebagai angka dengan split(".") dan Number, kedua ujung ikut rentan. Perbaikan yang masih masuk range ^ di package.json dipasang npm audit fix; yang butuh major baru perlu --force, changelog, dan test. Commit lockfile, pakai npm ci dan npm audit --audit-level=high di CI, dan nyalakan Dependabot.