Chapter Platform Requirements
mengajari kita menjaga gerbang lingkungan: versi PHP dan ekstensi yang salah tertangkap sebelum satu paket pun terpasang. Namun ada lapisan risiko yang lebih dalam: isi paket-paket itu sendiri. Sebagian besar baris kode aplikasi modern bukan tulisan kita, melainkan kode orang lain yang ditarik dari Packagist — dan celah keamanan pada dependensi ditemukan serta dipublikasikan hampir setiap hari. Composer menyediakan composer audit untuk membandingkan versi yang tercatat di composer.lock dengan basis data advisori keamanan. Pada chapter ini kita menjalankan audit pada proyek yang sehat maupun yang bermasalah, membaca laporannya, menangani paket yang ditinggalkan maintainer-nya, lalu mengunci seluruh proses itu sebagai gerbang wajib di pipeline CI.
Advisori Keamanan dan Rantai Pasok #
Ketika peneliti keamanan menemukan cacat pada sebuah library, temuan tersebut dipublikasikan sebagai advisori: dokumen yang memuat identitas celah (biasanya berupa nomor CVE), tingkat keparahan, rentang versi yang terdampak, dan versi yang sudah memperbaikinya. Composer mengambil daftar advisori ini dari Packagist, yang mengompilasi basis data FriendsOfPHP/security-advisories
beserta impor dari GitHub Advisory Database — itulah mengapa pada laporan audit Anda akan menjumpai dua jenis pengenal, CVE-... dan PKSA-....
Inti pekerjaan composer audit sederhana: bandingkan versi persis setiap paket di composer.lock dengan rentang versi pada setiap advisori. PHP sendiri menyediakan fungsi version_compare() yang menjadi dasar perbandingan semacam ini, sehingga kita bisa menyimulasikan mekanismenya secara mini:
<?php
function versiTerkena(string $versiTerpasang, string $operator, string $batas): bool
{
return version_compare($versiTerpasang, $batas, $operator);
}
$terpasang = [
'guzzlehttp/psr7' => '1.8.0',
'budi/pdf-generator' => '2.1.0',
];
$advisori = [
['paket' => 'guzzlehttp/psr7', 'operator' => '<', 'batas' => '1.8.4'],
['paket' => 'guzzlehttp/psr7', 'operator' => '<', 'batas' => '1.9.1'],
['paket' => 'budi/pdf-generator', 'operator' => '<', 'batas' => '2.0.0'],
];
foreach ($advisori as $info) {
$versi = $terpasang[$info['paket']];
$status = versiTerkena($versi, $info['operator'], $info['batas']) ? 'TERKENA' : 'aman';
printf("%-18s %-6s %-8s rentan %s %s\n", $info['paket'], $versi, $status, $info['operator'], $info['batas']);
}Output yang Dihasilkan:
guzzlehttp/psr7 1.8.0 TERKENA rentan < 1.8.4
guzzlehttp/psr7 1.8.0 TERKENA rentan < 1.9.1
budi/pdf-generator 2.1.0 aman rentan < 2.0.0Paket fiktif budi/pdf-generator sengaja dicampur untuk memperlihatkan kedua kemungkinan: versi 1.8.0 dari guzzlehttp/psr7 berada di bawah batas 1.8.4 dan 1.9.1 sehingga berstatus TERKENA, sedangkan 2.1.0 dari paket fiktif tersebut sudah di atas batas 2.0.0 sehingga aman. composer audit melakukan pemeriksaan yang sama untuk ratusan advisori dan seluruh paket di lock file secara otomatis — inilah alasan versi persis harus dikunci di composer.lock, bukan sekadar kendala di composer.json.
Menjalankan composer audit #
Kita mulai dari proyek yang sehat. Buat proyek kecil audit-demo dengan satu dependensi yang dipatok persis agar hasilnya dapat direproduksi:
{
"require": {
"psr/container": "2.0.2"
}
}Perhatikan bahwa composer update sebenarnya sudah menjalankan audit singkat di akhir prosesnya — baris yang pernah Anda lihat di chapter-chapter sebelumnya:
cd audit-demo
composer updateOutput yang Dihasilkan:
Loading composer repositories with package information
Updating dependencies
Lock file operations: 1 install, 0 updates, 0 removals
- Locking psr/container (2.0.2)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 1 install, 0 updates, 0 removals
- Installing psr/container (2.0.2): Extracting archive
Generating autoload files
No security vulnerability advisories found.Untuk pemeriksaan mandiri kapan pun dibutuhkan, jalankan perintahnya secara langsung:
composer auditOutput yang Dihasilkan:
No security vulnerability advisories found.Perintah composer audit mengembalikan exit code 0 ketika tidak ada temuan — perilaku inilah yang nanti kita manfaatkan di CI. Ada juga varian --locked yang memeriksa isi composer.lock tanpa menyentuh folder vendor, sehingga tetap berfungsi pada fresh checkout sebelum instalasi:
composer audit --lockedOutput yang Dihasilkan:
No security vulnerability advisories found.Composer Menolak Versi yang Sudah Diketahui Rentan #
Sebelum melihat laporan audit yang bermasalah, ada perilaku Composer 2.10 yang perlu Anda kenali. Ketika kita mencoba memasang versi yang sudah diketahui rentan, resolusi dependensi langsung menolaknya:
cd proyek-baru
composer require "guzzlehttp/psr7:1.8.0"Output yang Dihasilkan:
./composer.json has been updated
Running composer update guzzlehttp/psr7
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Root composer.json requires guzzlehttp/psr7 1.8.0 (exact version match: 1.8.0 or 1.8.0.0), found guzzlehttp/psr7[1.8.0] but these were not loaded, because they are affected by security advisories ("PKSA-vznr-tgp9-fd7d", "PKSA-7qs6-zvnz-h66r", "PKSA-gm5x-j3mz-71n9", "PKSA-jj5t-2zs1-dcfm", "PKSA-hn62-zkx4-1y5q", "PKSA-gvzg-s447-b5b5"). Go to https://packagist.org/security-advisories/ to find advisory details. To ignore the advisories, add their IDs to the "policy.advisories.ignore-id" config or add the package to "policy.advisories.ignore". To turn the feature off entirely, you can set "policy.advisories.block" to false.
Installation failed, reverting ./composer.json to its original content.Enam pengenal PKSA-... pada pesan tersebut adalah advisori yang menimpa versi 1.8.0. Penolakan ini dikendalikan oleh konfigurasi policy.advisories.block yang aktif secara default — kabar baik bagi proyek baru, karena versi rentan tidak akan pernah masuk. Untuk proyek lama yang lock filenya sudah berisi versi bermasalah, composer install dari lock tetap berjalan normal; pemasangan ulang tidaklah dicek ulang:
composer installOutput yang Dihasilkan:
Installing dependencies from lock file (including require-dev)
Verifying lock file contents can be installed on current platform.
Package operations: 3 installs, 0 updates, 0 removals
- Installing ralouphie/getallheaders (3.0.3): Extracting archive
- Installing psr/http-message (1.1): Extracting archive
- Installing guzzlehttp/psr7 (1.8.0): Extracting archive
Generating autoload filesJustru karena composer install bersikap patuh pada lock file, composer audit menjadi satu-satunya penjaga bagi proyek-proyek lama. Inilah alasan perintah ini wajib masuk rutinitas dan pipeline Anda.
Membaca Laporan Advisori #
Untuk membedah laporan, kita perlu proyek yang benar-benar membawa versi rentan. Bendera --no-security-blocking menonaktifkan penolakan di atas untuk satu perintah saja — bayangkan sedang menangani repositori lama yang belum sempat diperbarui:
cd proyek-lama
composer require "guzzlehttp/psr7:1.8.0" --no-security-blockingOutput yang Dihasilkan:
./composer.json has been updated
Running composer update guzzlehttp/psr7
Loading composer repositories with package information
Updating dependencies
Lock file operations: 3 installs, 0 updates, 0 removals
- Locking guzzlehttp/psr7 (1.8.0)
- Locking psr/http-message (1.1)
- Locking ralouphie/getallheaders (3.0.3)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 3 installs, 0 updates, 0 removals
- Installing ralouphie/getallheaders (3.0.3): Extracting archive
- Installing psr/http-message (1.1): Extracting archive
- Installing guzzlehttp/psr7 (1.8.0): Extracting archive
1 package suggestions were added by new dependencies, use `composer suggest` to see details.
Generating autoload files
Found 6 security vulnerability advisories affecting 1 package.
Run "composer audit" for a full list of advisories.Versi 1.8.0 berhasil terpasang, dan Composer menutup prosesnya dengan ringkasan singkat: enam advisori menimpa satu paket. Format ringkas seperti ini tersedia lewat --format=summary:
composer audit --format=summaryOutput yang Dihasilkan:
Found 6 security vulnerability advisories affecting 1 package.
Run "composer audit" for a full list of advisories.Perintah composer audit tanpa opsi mencetak seluruh advisori sebagai tabel per temuan. Berikut salinan entri pertama yang paling relevan dari enam entri pada mesin uji kami:
+-------------------+----------------------------------------------------------------------------------+
| Package | guzzlehttp/psr7 |
| Severity | medium |
| Advisory ID | PKSA-gvzg-s447-b5b5 |
| CVE | CVE-2022-24775 |
| Title | Inproper parsing of HTTP headers |
| URL | https://github.com/guzzle/psr7/security/advisories/GHSA-q7rv-6hp3-vh96 |
| Affected versions | >=2,<2.1.1|<1.8.4 |
| Reported at | 2022-03-20T13:44:44+00:00 |
+-------------------+----------------------------------------------------------------------------------+Mari kita bedah arti setiap barisnya:
Package— paket terpasang yang terdampak; versi persisnya dapat dicek dengancomposer show guzzlehttp/psr7.Severity— tingkat keparahan menurut penerbit advisori; gunakan untuk memprioritaskan, bukan untuk mengabaikan yang “low”.Advisory IDdanCVE— pengenal unik advisori;PKSAadalah pengenal Packagist,CVEadalah nomor standar internasional.URL— tautan ke advisori asli berisi teknis celah dan versi perbaikannya; selalu baca sebelum bertindak.Affected versions— rentang versi terdampak dalam notasi kendala Composer. Nilai>=2,<2.1.1|<1.8.4berarti dua kelompok rentan: seri 2.x di bawah 2.1.1, atau semua rilis di bawah 1.8.4 — versi 1.8.0 kita jatuh pada kelompok kedua. Notasi ini persis seperti yang kita pelajari di chapter Version Constraints .Reported at— tanggal advisori dipublikasikan.
Perintah ini mengembalikan exit code 1 selama ada satu saja advisori — sinyal yang keras dan berguna.
Paket yang Ditinggalkan #
Risiko kedua yang dilaporkan audit adalah paket abandoned: maintainer resmi menyatakan berhenti merawatnya, biasanya disertai saran pengganti. Kita bahas dengan paket nyata container-interop/container-interop yang memang sudah ditinggalkan:
cd proyek-lama
composer require "container-interop/container-interop:1.2.0"
composer showOutput yang Dihasilkan:
container-interop/container-interop 1.2.0 Promoting the interoperability of...
psr/container 1.1.2 Common Container Interface (PHP F...Menariknya, composer show memperlihatkan paket penggantinya ikut terpasang — container-interop memang bergantung pada psr/container. Composer juga mencetak peringatan “abandoned” saat pemasangan. Sekarang lihat bagaimana audit menilai kondisi ini:
composer auditOutput yang Dihasilkan:
No security vulnerability advisories found.
Found 1 abandoned package:
+-------------------------------------+----------------------------------------------------------------------------------+
| Abandoned Package | Suggested Replacement |
+-------------------------------------+----------------------------------------------------------------------------------+
| container-interop/container-interop | psr/container |
+-------------------------------------+----------------------------------------------------------------------------------+Tidak ada advisori keamanan, tetapi ada satu paket ditinggalkan — dan tetap dihitung sebagai temuan dengan exit code 1. Paket tanpa perawatan adalah risiko laten: celah yang ditemukan besok tidak akan pernah ditambal. Responsnya sederhana: migrasikan pemakaian ke paket pengganti yang disarankan, lalu hapus paket lama dari require.
Proses Respons: Perbarui, Uji, Komit #
Kembalilah ke proyek lama ber-guzzlehttp/psr7 1.8.0. Respons atas advisori mengikuti proses empat langkah yang disiplin:
- Baca advisori melalui URL pada laporan — pahami celahnya, versi perbaikannya, dan apakah aplikasi Anda benar-benar memakai jalur kode yang terdampak.
- Perbarui paket ke versi aman, selalu di dalam cakupan yang diizinkan kendala Anda.
- Uji seluruh test suite — pembaruan dependensi adalah perubahan kode juga.
- Komit
composer.lockyang baru agar seluruh tim dan server produksi menerima versi aman yang sama.
Langkah kedua kadang berjalan mulus, kadang menabrak dinding. composer.lock saat ini mematok 1.8.0 dan kendala di composer.json masih "1.8.0" persis, jadi kita perlonggar dulu menjadi "^1.8.0" lalu perbarui:
composer update guzzlehttp/psr7Output yang Dihasilkan:
Loading composer repositories with package information
Updating dependencies
Your requirements could not be resolved to an installable set of packages.
Problem 1
- Root composer.json requires guzzlehttp/psr7 ^1.8.0, found guzzlehttp/psr7[1.8.0, ..., 1.9.1] but these were not loaded, because they are affected by security advisories ("PKSA-vznr-tgp9-fd7d", "PKSA-7qs6-zvnz-h66r", "PKSA-gm5x-j3mz-71n9", "PKSA-jj5t-2zs1-dcfm", "PKSA-hn62-zkx4-1y5q", "PKSA-gvzg-s447-b5b5"). Go to https://packagist.org/security-advisories/ to find advisory details. To ignore the advisories, add their IDs to the "policy.advisories.ignore-id" config or add the package to "policy.advisories.ignore". To turn the feature off entirely, you can set "policy.advisories.block" to false.Dindingnya nyata: seluruh seri 1.x terdampak advisori termutakhir, sehingga jalan satu-satunya adalah lompatan mayor. Ubah kendala di composer.json menjadi "guzzlehttp/psr7": "^2.10", lalu perbarui bersama dependensinya:
{
"require": {
"guzzlehttp/psr7": "^2.10"
}
}composer update guzzlehttp/psr7 -W --no-progressOutput yang Dihasilkan:
Loading composer repositories with package information
Updating dependencies
Lock file operations: 3 installs, 2 updates, 0 removals
- Upgrading guzzlehttp/psr7 (1.8.0 => 2.13.1)
- Locking psr/http-factory (1.1.0)
- Upgrading psr/http-message (1.1 => 2.0)
- Locking symfony/deprecation-contracts (v3.7.1)
- Locking symfony/polyfill-php80 (v1.43.0)
Writing lock file
Installing dependencies from lock file (including require-dev)
Package operations: 3 installs, 2 updates, 0 removals
- Installing symfony/polyfill-php80 (v1.43.0): Extracting archive
- Installing symfony/deprecation-contracts (v3.7.1): Extracting archive
- Upgrading psr/http-message (1.1 => 2.0): Extracting archive
- Installing psr/http-factory (1.1.0): Extracting archive
- Upgrading guzzlehttp/psr7 (1.8.0 => 2.13.1): Extracting archive
Generating autoload files
3 packages you are using are looking for funding.
Use the `composer fund` command to find out more!
No security vulnerability advisories found.Lompatan dari 1.x ke 2.x membawa perubahan API — psr/http-message naik ke 2.0 dan paket pendukung baru ikut masuk. Di sinilah langkah ketiga proses (uji) menjadi penentu: jalankan test suite sampai hijau, perbaiki pemakaian API yang berubah, baru komit lock. Penomoran versi mayor dan arti janji kompatibilitasnya sudah kita bicarakan tuntas di chapter Version Constraints , dan otomasi berjalan lewat scripts pada chapter Composer Scripts .
Mengunci Audit di Pipeline CI #
Pemeriksaan yang bergantung pada kesadaran manusia pasti terlewat. Karena itu audit ditempatkan sebagai gerbang di pipeline CI — berjalan pada setiap push, sebelum test suite, dan membuat pipeline gagal merah begitu exit code tidak nol:
name: CI
on: [push, pull_request]
jobs:
tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: shivammathur/setup-php@v2
with:
php-version: "8.3"
- name: Pasang dependensi dari lock
run: composer install --no-interaction --no-progress
- name: Audit dependensi
run: composer audit --locked
- name: Jalankan test suite
run: composer testLangkah Audit dependensi sengaja berdiri sendiri dan ditempatkan sebelum test suite — advisor ditemukan lebih cepat daripada menunggu rangkaian pengujian panjang selesai. Opsi --locked memastikan yang diperiksa adalah persis isi lock yang akan dipasang di produksi. Banyak tim juga menambahkan pekerjaan terjadwal mingguan yang menjalankan composer update dan audit untuk mendeteksi advisori baru pada dependensi yang tidak berubah, atau memakai bot pembaru dependensi seperti Dependabot yang otomatis membuka pull request ketika ada rilis aman baru.
Ringkasan #
composer audit membandingkan versi persis di composer.lock dengan basis data advisori Packagist dan GitHub, melaporkan advisori keamanan maupun paket yang ditinggalkan, serta mengembalikan exit code 1 untuk setiap temuan sehingga mudah dijadikan gerbang CI. Composer 2.10 bahkan menolak resolusi ke versi yang sudah diketahui rentan sejak awal. Proses responsnya disiplin: baca advisori, perbarui paket — kadang dengan lompatan mayor — uji, lalu komit lock baru. Setelah keamanan dependensi publik terjaga, pertanyaan berikutnya adalah bagaimana menangani kode privat perusahaan yang tidak boleh hidup di Packagist — topik chapter Private Packages
.