Episode ini membahas risiko kelas dinamis dari input pengguna, pola validasi dan safelist yang aman, serta cara menghindari injection via class names. Kalian juga belajar strategi content scanning untuk environment multi-tenant dengan isolasi yang benar.

Konten dinamis adalah keniscayaan di aplikasi nyata: warna dari database, ukuran dari preferensi pengguna, tema dari konfigurasi. Tapi cara kalian menggabungkan nilai itu ke class attribute membawa dua risiko: kelas tidak terbangkitkan oleh JIT, dan yang lebih serius — CSS injection lewat class name. Episode 11 membahas keduanya.
Risiko pertama membuat UI tampil tanpa gaya; risiko kedua bisa menjadi celah keamanan. Tailwind mendukung nilai arbitrer seperti p-[calc(100%-1rem)], dan jika nilai itu datang dari pengguna tanpa validasi, pengguna bisa menyuntikkan arbitrary value berbahaya ke dalam stylesheet kalian.
Pola paling bermasalah adalah merangkai nama kelas dari potongan string:
const size = userInput; // nilai dari database atau form
const className = "p-" + size; // berbahayaDua masalah muncul: JIT tidak melihat string lengkap p-4 di content, jadi kelas tidak dibangkitkan; dan jika size berisi nilai arbitrer, pengguna bisa menyuntikkan aturan CSS sembarangan melalui fitur arbitrary values. Hindari pola ini sepenuhnya.
Gantikan perangkaian dengan mapping eksplisit — setiap nilai yang diizinkan memetakan ke string kelas yang sudah ditulis lengkap:
const sizeMap = {
small: "p-2 text-sm",
medium: "p-4 text-base",
large: "p-6 text-lg",
};
const className = sizeMap[size] ?? sizeMap.medium;String kelas seperti "p-4 text-base" muncul utuh di content, sehingga JIT membangkitkannya. Nilai yang tidak dikenal jatuh ke default sizeMap.medium. Pola sizeMap[size] ?? sizeMap.medium ini aman, deterministik, dan mudah diuji.
Jika mapping tidak memungkinkan karena jumlah kombinasi besar tapi terbatas, gunakan safelist:
safelist: [
"bg-red-500",
"bg-green-500",
"bg-blue-500",
"text-red-500",
"text-green-500",
"text-blue-500",
],Safelist memaksa kelas tersebut selalu dibangkitkan. Batasi penggunaannya — setiap entri menambah ukuran CSS tanpa peduli dipakai atau tidak.
CSS injection terjadi saat nilai pengguna masuk ke class attribute dan Tailwind menerimanya sebagai arbitrary value. Dua lapis pertahanan:
[, :, dan ;.Contoh validasi sederhana:
const allowedSizes = new Set(["small", "medium", "large"]);
const safeSize = allowedSizes.has(size) ? size : "medium";allowedSizes.has(size) ? size : "medium" menjamin hanya tiga nilai yang pernah sampai ke markup. Tidak ada jalan bagi arbitrary value dari pengguna untuk masuk ke class attribute.
Warning
Jangan pernah menempatkan data pengguna langsung ke dalam class attribute. Jika aplikasi kalian memang harus menerima nama kelas dari luar, validasi terhadap allowlist dulu, lalu render sebagai string lengkap — bukan potongan yang dirangkai.
Di environment multi-tenant, satu instance Tailwind melayani banyak tenant dengan tema berbeda. Tantangannya: jika seluruh tenant di-scan bersama, semua kelas semua tenant masuk ke satu stylesheet — boros dan berisiko bocornya token antar tenant.
Strategi yang direkomendasikan:
Contoh build per tenant di CLI:
npx tailwindcss -i ./themes/tenant-a.css -o ./dist/tenant-a.css
npx tailwindcss -i ./themes/tenant-b.css -o ./dist/tenant-b.cssSetiap tenant mendapat file CSS terpisah yang hanya memuat kelas yang diizinkan untuknya — hasil scanning yang benar-benar terisolasi.
Di environment multi-tenant dengan SSR atau API-driven, pastikan daftar content hanya memuat file yang benar-benar menulis nama kelas — template dan komponen, bukan data runtime. Memindai isi database menambah beban build tanpa manfaat, karena JIT hanya mengenali string literal di source, bukan nilai yang disuntikkan dari luar.
Episode 11 menutup celah keamanan dan keandalan konten dinamis: mapping eksplisit menggantikan perangkaian nama kelas, safelist untuk set terbatas, validasi allowlist untuk menghalau injection, dan build per tenant untuk isolasi di multi-tenant.
Inti yang harus dibawa pulang:
Di episode 12 selanjutnya kita akan membahas delivery & CDN — perbandingan build-time generation dengan Play CDN, keamanan CSP dan SRI saat memakai CDN, serta strategi caching dan HTTP headers untuk file CSS statis.