Aplikasi Command Line Interface (CLI) untuk visualisasi data tabular seperti grafik ASCII atau tabel interaktif di terminal sering kali mengasumsikan input file lokal berada dalam kendali pengguna tepercaya. Namun, ketika CLI memproses dataset tidak tepercaya (untrusted CSV) yang diunduh dari web atau dioperasikan dalam pipeline otomatis, kerentanan seperti alokasi memori tak terbatas (Denial of Service) dan Terminal Escape Injection (ANSI escape sequences) dapat membahayakan integritas terminal serta ketersediaan sistem.
Artikel ini membahas praktik hardening parser dan visualizer terminal di Rust menggunakan pendekatan streaming terikat, pembersihan karakter kontrol, serta konversi tipe data numerik yang aman.
Vektor Serangan Input CSV pada Aplikasi CLI
Saat membangun tool berbasis Rust seperti clone xan, serangan tipikal pada parsing dan rendering CSV mencakup tiga area utama:
- Memory Exhaustion (OOM): File CSV dengan baris atau field tunggal berukuran gigabyte tanpa batas delimitasi dapat memaksa buffer memori membengkak hingga proses dimatikan oleh OOM Killer sistem operasi.
- Terminal Escape Injection (ANSI Bombs): Kolom teks yang mengandung sekuens escape seperti
\x1B]0;...\x07atau manipulasi kursor ANSI dapat mengubah judul window terminal, menyembunyikan teks, atau memanipulasi buffer terminal saat dicetak kestdout. - Numeric Parsing DoS: Parsing string arbitrer ke tipe floating point standar (
f64) yang tidak menangani status nilai khusus sepertiNaN,Infinity, atau representasi floating-point non-standar yang memicu kalkulasi tak terduga pada skala visualisasi.
1. Mitigasi Alokasi Memori dengan Bounded Streaming
Membaca seluruh konten CSV ke dalam memori via std::fs::read_to_string adalah anti-pattern untuk CLI produksi. Kita wajib menggunakan pembacaan berbasis aliran (streaming) dengan batas eksplisit ukuran record dan kapasitas field menggunakan crate csv standar.
use csv::ReaderBuilder;
use std::fs::File;
use std::io::{self, BufReader, Read};
const MAX_RECORD_SIZE_BYTES: usize = 64 * 1024; // Batas per record: 64 KB
pub fn get_hardened_csv_reader(path: &str) -> io::Result<csv::Reader<impl Read>> {
let file = File::open(path)?;
// Bungkus pembacaan dengan buffer terbatas
let buffered = BufReader::with_capacity(128 * 1024, file);
let reader = ReaderBuilder::new()
.has_headers(true)
.flexible(false) // Tolak baris yang jumlah field-nya tidak konsisten
.buffer_capacity(MAX_RECORD_SIZE_BYTES)
.from_reader(buffered);
Ok(reader)
}
Pengaturan flexible(false) memastikan anomali struktural pada baris CSV langsung menghasilkan error di awal eksekusi, mencegah visualisasi data yang korup.
2. Pencegahan Terminal Escape Injection
Karakter ANSI escape sequence memungkinkan teks input memanipulasi emulator terminal pengguna. Untuk visualisasi terminal yang aman, seluruh field string teks yang akan dicetak sebagai label atau header wajib disanitasi dari karakter kontrol ASCII (0x00-0x1F dan 0x7F), terutama byte ESC (0x1B).
pub fn sanitize_terminal_output(raw: &str) -> String {
raw.chars()
.filter_map(|c| {
// Izinkan spasi biasa, tolak semua karakter kontrol ASCII
if c.is_ascii_control() {
None
} else {
Some(c)
}
})
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_strip_ansi_and_controls() {
let malicious_payload = "\x1B[31;1mRedText\x1B[0m\nInject";
let clean = sanitize_terminal_output(malicious_payload);
assert_eq!(clean, "RedTextInject");
}
}
Jika kebutuhan visualisasi memerlukan penanganan teks Unicode multibyte (misal: aksara non-Latin atau emoji), sanitasi harus tetap mempertahankan validitas UTF-8 sembari memfilter blok kontrol Unicode (C0 dan C1 control codes).
3. Safe Numeric Parsing untuk Plotting
Komponen plot grafik membutuhkan representasi numerik. Fungsi bawaan str::parse::<f64>() dapat meloloskan nilai NaN, inf, atau -inf yang merusak kalkulasi rentang minimum/maksimum kanvas terminal. Rust menyediakan method validasi numerik bawaan tanpa dependensi eksternal.
#[derive(Debug, PartialEq)]
pub enum NumericError {
InvalidFormat,
NonFiniteValue,
}
pub fn parse_safe_coordinate(input: &str) -> Result<f64, NumericError> {
let val: f64 = input.trim().parse().map_err(|_| NumericError::InvalidFormat)?;
// Cegah nilai tak terhingga dan Not-a-Number merusak auto-scaler visualisasi
if !val.is_finite() {
return Err(NumericError::NonFiniteValue);
}
Ok(val)
}
Implementasi Penuh Pipeline Pembacaan Terproteksi
Berikut adalah implementasi minimal integrasi pembacaan, validasi, dan sanitasi data yang siap dirender ke kanvas visualisasi terminal:
use csv::StringRecord;
use std::error::Error;
pub struct ValidatedRow {
pub label: String,
pub value: f64,
}
pub fn process_csv_for_chart(path: &str) -> Result<Vec<ValidatedRow>, Box<dyn Error>> {
let mut reader = get_hardened_csv_reader(path)?;
let mut record = StringRecord::new();
let mut results = Vec::new();
// Membaca record demi record menggunakan buffer yang dialokasikan sekali
while reader.read_record(&mut record)? {
if record.len() < 2 {
continue; // Abaikan atau lempar format error
}
let raw_label = &record[0];
let raw_value = &record[1];
let label = sanitize_terminal_output(raw_label);
let value = parse_safe_coordinate(raw_value)?;
results.push(ValidatedRow { label, value });
}
Ok(results)
}
Catatan Kinerja: Hindari memanggil
reader.records()iterator langsung dalam loop jika dataset berukuran masif, karena metode tersebut mengalokasikanStringRecordbaru pada setiap baris. Pendekatanreader.read_record(&mut record)mendaur ulang buffer alokasi internal secara efisien.
Ringkasan Trade-Off
Penerapan validasi ketat ini memiliki konsekuensi fungsionalitas:
- Penolakan Format Eksotis: Menghapus karakter kontrol akan menghilangkan format styling sah jika CSV memang sengaja memuat format escape terformat (kasus sangat langka).
- Validitas Floating Point: Menolak
NaNmemutus kontinuitas plot garis data kosong. Jika nilai kosong diizinkan, transformasikanNaNmenjadiOption<f64>::Nonealih-alih melempar error langsung.
Menerapkan pembatasan alokasi, pembersihan kontrol karakter terminal, dan pengetatan floating-point menghasilkan CLI visualisasi Rust yang tangguh, deterministik, dan aman terhadap eksploitasi data lokal maupun pipeline otomatis.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!