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>
This commit is contained in:
1 parent
df27974359
commit
791538c9d9
112 files changed
+15620
-494
No files matched your search
@@ -0,0 +1,135 @@
|
||||
//! 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());
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user