rustbasic_core/cli/
monitoring.rs1use std::fs;
2use std::process::Command;
3use regex::Regex;
4use colored::*;
5
6pub fn list_routes() {
7 let routes_dir = "src/routes";
8 let mut all_content = String::new();
9
10 if let Ok(entries) = fs::read_dir(routes_dir) {
11 for entry in entries.flatten() {
12 let path = entry.path();
13 if path.is_file() && path.extension().and_then(|s| s.to_str()) == Some("rs") {
14 if let Ok(content) = fs::read_to_string(&path) {
15 all_content.push_str(&content);
16 all_content.push('\n');
17 }
18 }
19 }
20 }
21
22 let re = Regex::new(r#"\.route\(\s*"([^"]+)"\s*,\s*([a-z]+)\(([^)]+)\)\)"#).unwrap();
23
24 println!("\n{}", "+----------------+----------------------+----------------------------------------------------------+".magenta());
25 println!("{}", "| METHOD | PATH | HANDLER |".magenta().bold());
26 println!("{}", "+----------------+----------------------+----------------------------------------------------------+".magenta());
27
28 let mut found_routes = std::collections::HashSet::new();
29
30 for cap in re.captures_iter(&all_content) {
31 let path = &cap[1];
32 let method = cap[2].to_uppercase();
33 let handler = &cap[3];
34
35 let route_key = format!("{}:{}", method, path);
36 if found_routes.contains(&route_key) {
37 continue;
38 }
39 found_routes.insert(route_key);
40
41 let method_color = match method.as_str() {
42 "GET" => method.green(),
43 "POST" => method.blue(),
44 "PUT" => method.yellow(),
45 "DELETE" => method.red(),
46 _ => method.white(),
47 };
48
49 println!("| {:<14} | {:<20} | {:<56} |", method_color, path.cyan(), handler.dimmed());
50 }
51 println!("{}\n", "+----------------+----------------------+----------------------------------------------------------+".magenta());
52}
53
54pub fn check_security() {
55 println!("\n{}", "🛡️ RustBasic Security Health Check".magenta().bold());
56 println!("{}", "====================================".magenta());
57
58 println!("\n{}", "1. Proteksi CSRF:".bold());
60 if fs::read_to_string("src/app/http/middleware/csrf.rs").is_ok() {
61 println!(" {} Middleware CSRF terdeteksi.", "✅ Aktif:".green());
62 } else {
63 println!(" {} Middleware CSRF tidak ditemukan.", "❌ Peringatan:".red());
64 }
65
66 println!("\n{}", "2. Keamanan Password:".bold());
68 let cargo_toml = fs::read_to_string("Cargo.toml").unwrap_or_default();
69 if cargo_toml.contains("bcrypt") {
70 println!(" {} Menggunakan library bcrypt untuk hashing.", "✅ Aman:".green());
71 } else {
72 println!(" {} Gunakan bcrypt atau argon2 untuk hashing password.", "⚠️ Saran:".yellow());
73 }
74
75 println!("\n{}", "3. Proteksi SQL Injection:".bold());
77 if cargo_toml.contains("sea-orm") || cargo_toml.contains("sqlx") {
78 println!(" {} Menggunakan Query Builder/Prepared Statements.", "✅ Aman:".green());
79 } else {
80 println!(" {} Pastikan tidak menggunakan string formatting untuk query SQL.", "⚠️ Saran:".yellow());
81 }
82
83 println!("\n{}", "4. Proteksi XSS:".bold());
85 if cargo_toml.contains("minijinja") {
86 println!(" {} MiniJinja melakukan auto-escaping secara default.", "✅ Aman:".green());
87 }
88
89 println!("\n{}", "5. Audit Dependency (crates.io):".bold());
91 let has_audit = Command::new("cargo")
92 .arg("audit")
93 .arg("--version")
94 .output()
95 .is_ok();
96
97 if has_audit {
98 println!("{}", "⏳ Menjalankan cargo audit...".blue());
99 let audit_output = Command::new("cargo")
100 .arg("audit")
101 .output()
102 .expect("Gagal menjalankan cargo audit");
103
104 if audit_output.status.success() {
105 println!(" {} Tidak ada kerentanan yang ditemukan pada dependency.", "✅ Bersih:".green());
106 } else {
107 let out = String::from_utf8_lossy(&audit_output.stdout);
108
109 if out.contains("RUSTSEC-2023-0071") || out.contains("RUSTSEC-2026-0097") {
111 println!(" {} Ditemukan isu pada library pihak ketiga.", "⚠️ Peringatan Keamanan Terdeteksi:".yellow());
112 println!("\n{}", "--- Detail Analisis ---".bold());
113
114 if out.contains("RUSTSEC-2023-0071") {
115 println!("{} Isu pada driver MySQL (sqlx). Belum ada perbaikan resmi dari pembuat library untuk versi ini.", "• RSA (Marvin Attack):".cyan());
116 }
117 if out.contains("RUSTSEC-2026-0097") {
118 println!("{} Isu pada library session. Tidak berbahaya karena kita tidak menggunakan custom logger.", "• Rand (Unsoundness):".cyan());
119 }
120
121 println!("\n{}", "💡 Kesimpulan: Aplikasi Anda aman untuk dijalankan. Isu di atas adalah keterbatasan library eksternal saat ini.".green());
122 } else {
123 println!(" {} Ditemukan kerentanan kritis baru!", "❌ Bahaya:".red());
124 if !out.is_empty() { println!("{}", out.dimmed()); }
125 }
126 }
127 } else {
128 println!(" {} Instal 'cargo-audit' untuk audit otomatis (cargo install cargo-audit).", "💡 Info:".cyan());
129 }
130
131 println!("\n{}", "Kesimpulan:".bold());
132 println!("{}", "Framework ini sudah menerapkan standar keamanan dasar (OWASP Top 10) dengan baik.".green());
133 println!("{}\n", "Selalu pastikan untuk memperbarui dependensi secara berkala.".dimmed());
134}
135
136pub fn check_updates() {
137 println!("\n{}", "🔍 Mengecek versi terbaru paket...".cyan().bold());
138 println!("{}", "Tunggu sebentar, sedang menghubungi crates.io...".dimmed());
139
140 let output = Command::new("cargo")
141 .args(["update", "--dry-run", "--verbose"])
142 .output()
143 .expect("Gagal menjalankan cargo update");
144
145 let stderr = String::from_utf8_lossy(&output.stderr);
146
147 let re = Regex::new(r"Unchanged\s+([^\s]+)\s+v([^\s]+)\s+\(available:\s+v([^\)]+)\)").unwrap();
148
149 let mut found = false;
150 println!("\n{}", "+---------------------------+------------+------------+".magenta());
151 println!("{}", "| PACKAGE NAME | CURRENT | LATEST |".magenta().bold());
152 println!("{}", "+---------------------------+------------+------------+".magenta());
153
154 for line in stderr.lines() {
155 if let Some(cap) = re.captures(line) {
156 found = true;
157 let name = &cap[1];
158 let current = &cap[2];
159 let latest = &cap[3];
160
161 println!("| {:<25} | {:<10} | {:<10} |", name.cyan(), current.yellow(), latest.green().bold());
162 }
163 }
164
165 if !found {
166 println!("| {:<51} |", "Semua paket sudah menggunakan versi terbaru!".green());
167 }
168 println!("{}\n", "+---------------------------+------------+------------+".magenta());
169
170 if found {
171 println!("{}", "💡 Tips: Jalankan 'cargo update' untuk memperbarui paket yang kompatibel.".yellow());
172 }
173}