Files
sabia/crates/sabia-core/src/doctor.rs
T
Lucas RayanandClaude Opus 5.5 791538c9d9 v0.1.0: logo, i18n pt/en, CI com caches, testes e documentação
- Logo oficial (sabia.png) e conjunto de ícones do app desktop.
- Interface em português e inglês: tradutor por observação do DOM, seletor
  de idioma e checagem de cobertura de tradução no CI.
- CI/CD com caches (rust-cache, .deb do Tauri em cache) e pipeline de release
  multiplataforma (CLI em 5 alvos + app desktop via tauri-action), templates
  de issue/PR.
- Suíte de testes de integração do sabia-core (scan e2e, handshake/crack
  4-way e PMKID, pcap, trilha de auditoria) — 46 testes verdes.
- Documentação completa pt/en: README, ROADMAP, SECURITY, CONTRIBUTING,
  CHANGELOG e docs/ (cli, interface, arquitetura), com enquadramento de uso
  autorizado.
- Correções de clippy (-D warnings) e formatação em toda a árvore.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 12:46:19 -03:00

136 lines
4.0 KiB
Rust

//! Diagnóstico do ambiente: privilégios, ferramentas externas, interfaces,
//! suporte a modo monitor e integridade da trilha de auditoria.
use crate::audit::AuditLog;
use crate::iface::{self, Interface};
use crate::netlink::{self, command_exists};
use std::path::{Path, PathBuf};
#[derive(Debug, Clone, serde::Serialize)]
pub struct IfaceDiagnosis {
pub name: String,
pub mac: String,
pub mode: String,
pub monitor_supported: bool,
pub driver: Option<String>,
pub up: bool,
}
#[derive(Debug, Clone, serde::Serialize)]
pub struct DoctorReport {
pub version: String,
pub platform: String,
pub root: bool,
pub iw: bool,
pub ip: bool,
pub curl: bool,
pub capture_backend: String,
pub audit_path: String,
pub audit_ok: bool,
pub interfaces: Vec<IfaceDiagnosis>,
pub notes: Vec<String>,
}
pub fn diagnose(version: &str, audit_path: impl AsRef<Path>) -> DoctorReport {
let audit_path = audit_path.as_ref().to_path_buf();
let root = netlink::is_root();
let iw = command_exists("iw");
let ip = command_exists("ip");
let curl = command_exists("curl");
let (audit_ok, mut notes) = audit_status(&audit_path);
let all: Vec<Interface> = iface::list_all().unwrap_or_default();
let interfaces = all
.into_iter()
.filter(|i| i.phy.is_some())
.map(|i| IfaceDiagnosis {
monitor_supported: netlink::monitor_supported(&i.name),
driver: netlink::driver_name(&i.name),
name: i.name,
mac: i.mac,
mode: format!("{:?}", i.mode).to_lowercase(),
up: i.up,
})
.collect::<Vec<_>>();
if !root {
notes.push(
"sem root: modo monitor, captura e injeção vão falhar (use sudo ou o app desktop)"
.into(),
);
}
if !iw {
notes.push("`iw` não encontrado no PATH — instale iw/iproute2".into());
}
if !ip {
notes.push("`ip` não encontrado no PATH — instale iproute2".into());
}
if interfaces.is_empty() {
notes.push("nenhuma interface Wi-Fi encontrada".into());
} else if !interfaces.iter().any(|i| i.monitor_supported) {
notes.push("nenhuma interface anuncia suporte a modo monitor".into());
}
if !curl {
notes.push("`curl` não encontrado — a auto-atualização não funciona".into());
}
if !audit_ok {
notes.push("a trilha de auditoria existente está corrompida".into());
}
DoctorReport {
version: version.to_string(),
platform: format!("{}-{}", std::env::consts::OS, std::env::consts::ARCH),
root,
iw,
ip,
curl,
capture_backend: if cfg!(target_os = "linux") {
"AF_PACKET (Linux)".into()
} else {
"indisponível nesta plataforma".into()
},
audit_path: audit_path.display().to_string(),
audit_ok,
interfaces,
notes,
}
}
fn audit_status(path: &PathBuf) -> (bool, Vec<String>) {
let mut notes = Vec::new();
if !path.exists() {
notes.push(format!(
"trilha de auditoria ainda não criada em {}",
path.display()
));
return (true, notes);
}
match AuditLog::verify(path) {
Ok(None) => (true, notes),
Ok(Some(idx)) => {
notes.push(format!("cadeia de auditoria rompida na entrada {idx}"));
(false, notes)
}
Err(e) => {
notes.push(format!("não foi possível ler a trilha de auditoria: {e}"));
(false, notes)
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use tempfile::TempDir;
#[test]
fn reports_environment_without_panicking() {
let dir = TempDir::new().unwrap();
let report = diagnose("9.9.9", dir.path().join("nao-existe.log"));
assert_eq!(report.version, "9.9.9");
assert!(report.audit_ok);
assert!(report.platform.contains('-'));
assert!(!report.capture_backend.is_empty());
}
}