App desktop (Tauri), GitHub Actions de CI/release, elevação automática
- Move a UI de crates/sabia-cli/src/web/ para apps/gui/ui/: fonte única servida tanto pelo `sabia ui` quanto pelo app desktop. - sabia-core::engine::Engine centraliza sessões de scan/monitor/update — usado pelo servidor local da CLI e pelo app Tauri, sem duplicar lógica. - apps/gui/src-tauri: app desktop Tauri 2, mesma interface, comandos IPC espelhando a API HTTP. Pede elevação de administrador/root automaticamente ao abrir (modo monitor e captura crua exigem): manifesto requireAdministrator no Windows (UAC antes mesmo do app abrir), pkexec no Linux e osascript no macOS, com reinício automático e aviso + botão de nova tentativa na interface se for negada. - Ícone gerado a partir do logo (apps/gui/src-tauri/icons/source.png). - .github/workflows/ci.yml: fmt, clippy, testes (Linux/Windows/macOS), auditoria de dependências, compilação do app desktop. - .github/workflows/release.yml: ao empurrar uma tag vX.Y.Z, builda a CLI (Linux x86_64/aarch64, macOS x86_64/aarch64, Windows x86_64) e o app desktop pras mesmas plataformas, publica tudo numa release do GitHub com checksums SHA-256. - sabia-core::update: corrige o nome do asset esperado (casava pelo nome fixo, mas o release empacota com a versão no nome) e a extração (tar.gz no Linux/macOS, zip no Windows). - LICENSE-MIT, LICENSE-APACHE, CHANGELOG.md com a entrada 0.1.0. - cargo fmt + clippy (-D warnings) limpos em todo o workspace.
This commit is contained in:
1 parent
fd3ef5240e
commit
df27974359
29 files changed
+1268
-191
No files matched your search
@@ -1,14 +1,20 @@
|
||||
mod web;
|
||||
|
||||
use clap::{Parser, Subcommand};
|
||||
use sabia_core::{audit::AuditLog, capture::RawCapture, iface, netlink, pcapwriter::PcapWriter, scan::ScanTable};
|
||||
use sabia_core::{
|
||||
audit::AuditLog, capture::RawCapture, iface, netlink, pcapwriter::PcapWriter, scan::ScanTable,
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
use std::sync::atomic::{AtomicBool, Ordering};
|
||||
use std::sync::Arc;
|
||||
use std::time::Duration;
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(name = "sabia", about = "Sabiá — auditoria e monitoramento de redes Wi-Fi", version)]
|
||||
#[command(
|
||||
name = "sabia",
|
||||
about = "Sabiá — auditoria e monitoramento de redes Wi-Fi",
|
||||
version
|
||||
)]
|
||||
struct Cli {
|
||||
#[command(subcommand)]
|
||||
cmd: Cmd,
|
||||
@@ -52,7 +58,11 @@ enum Cmd {
|
||||
enum IfaceAction {
|
||||
List,
|
||||
/// Liga/desliga modo monitor numa interface Wi-Fi.
|
||||
Monitor { iface: String, #[arg(value_enum)] state: OnOff },
|
||||
Monitor {
|
||||
iface: String,
|
||||
#[arg(value_enum)]
|
||||
state: OnOff,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, clap::ValueEnum)]
|
||||
@@ -94,23 +104,31 @@ fn run(cli: Cli) -> sabia_core::Result<()> {
|
||||
iface::get(&name)?; // valida que é Wi-Fi antes de tocar nela
|
||||
netlink::set_monitor_mode(&name, enable)?;
|
||||
let mut log = AuditLog::open(audit_path())?;
|
||||
log.record(
|
||||
"monitor_mode",
|
||||
&name,
|
||||
&format!("{{\"enable\": {enable}}}"),
|
||||
)?;
|
||||
println!("{name}: modo monitor {}", if enable { "ativado" } else { "desativado" });
|
||||
log.record("monitor_mode", &name, &format!("{{\"enable\": {enable}}}"))?;
|
||||
println!(
|
||||
"{name}: modo monitor {}",
|
||||
if enable { "ativado" } else { "desativado" }
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
Cmd::Scan { iface: name, channel, seconds, out } => {
|
||||
Cmd::Scan {
|
||||
iface: name,
|
||||
channel,
|
||||
seconds,
|
||||
out,
|
||||
} => {
|
||||
let info = iface::get(&name)?;
|
||||
if info.mode != iface::Mode::Monitor {
|
||||
eprintln!("aviso: {name} não está em modo monitor (sabia iface monitor {name} on)");
|
||||
}
|
||||
|
||||
let mut log = AuditLog::open(audit_path())?;
|
||||
log.record("scan_start", &name, &format!("{{\"channel\": {channel:?}}}"))?;
|
||||
log.record(
|
||||
"scan_start",
|
||||
&name,
|
||||
&format!("{{\"channel\": {channel:?}}}"),
|
||||
)?;
|
||||
|
||||
let cap = RawCapture::open(&name)?;
|
||||
cap.set_read_timeout(Duration::from_millis(500))?;
|
||||
@@ -143,7 +161,10 @@ fn run(cli: Cli) -> sabia_core::Result<()> {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if channel.is_none() && !channels.is_empty() && last_hop.elapsed() > Duration::from_millis(300) {
|
||||
if channel.is_none()
|
||||
&& !channels.is_empty()
|
||||
&& last_hop.elapsed() > Duration::from_millis(300)
|
||||
{
|
||||
chan_idx = (chan_idx + 1) % channels.len();
|
||||
let _ = netlink::set_channel(&name, channels[chan_idx]);
|
||||
last_hop = std::time::Instant::now();
|
||||
@@ -167,16 +188,25 @@ fn run(cli: Cli) -> sabia_core::Result<()> {
|
||||
w.flush()?;
|
||||
}
|
||||
|
||||
log.record("scan_stop", &name, &format!("{{\"aps_found\": {}}}", table.access_points().len()))?;
|
||||
log.record(
|
||||
"scan_stop",
|
||||
&name,
|
||||
&format!("{{\"aps_found\": {}}}", table.access_points().len()),
|
||||
)?;
|
||||
|
||||
println!("\n{:<18} {:<24} {:<8} {:<8} {:<6} {}", "BSSID", "SSID", "CANAL", "SINAL", "SEG", "CLIENTES");
|
||||
println!(
|
||||
"\n{:<18} {:<24} {:<8} {:<8} {:<6} CLIENTES",
|
||||
"BSSID", "SSID", "CANAL", "SINAL", "SEG"
|
||||
);
|
||||
for ap in table.access_points() {
|
||||
println!(
|
||||
"{:<18} {:<24} {:<8} {:<8} {:<6} {}",
|
||||
ap.bssid,
|
||||
ap.ssid.unwrap_or_default(),
|
||||
ap.channel.map(|c| c.to_string()).unwrap_or_default(),
|
||||
ap.last_signal_dbm.map(|s| format!("{s}dBm")).unwrap_or_default(),
|
||||
ap.last_signal_dbm
|
||||
.map(|s| format!("{s}dBm"))
|
||||
.unwrap_or_default(),
|
||||
ap.security.unwrap_or_default(),
|
||||
ap.clients.len()
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user