fix(gui): add non-Linux stubs for pmkid_hunt/pmf_test and fix Windows warnings

main.rs failed to compile on Windows: pmkid_hunt and pmf_test were only
implemented under #[cfg(target_os = "linux")], with no fallback stub,
so the Tauri commands calling them did not resolve.

- Add #[cfg(not(target_os = "linux"))] stubs for both, matching the
  existing pattern for every other Linux-only Engine method (returns
  SabiaError::UnsupportedPlatform).
- Mark the DeauthJob/Engine fields and session_ap/client_snapshot
  helpers that only the Linux-only code paths use with
  #[cfg_attr(not(target_os = "linux"), allow(dead_code))], same for
  eviltwin.rs's form_value/url_decode (only called from the
  #[cfg(target_os = "linux")] HTTP handler) — silences the 5
  dead_code warnings a Windows build produced.
- .setup(|app| ...) used app only inside unix/linux/macos-gated
  blocks, so it was unused on Windows; renamed the closure parameter
  and reintroduce it under the matching cfg.
- Add the missing English dictionary entry for the mutation-rules
  checkbox label in app.js (found by visually reviewing a generated
  screenshot: it was rendering in Portuguese in the English UI even
  though check-ui-i18n.mjs reported full coverage).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VAyr8d7HuAdUGB6aA5xmDD
This commit is contained in:
Lucas RayanandClaude Sonnet 5 committed 2026-10-30 20:44:17 -03:00
1 parent 2dfbbfc64a
commit a904723f96
4 files changed
+302 -270

No files matched your search

+20 -17
View File
@@ -1,4 +1,4 @@
// Evita a janela de console extra no Windows em release.
// Prevents an extra console window on Windows in release builds.
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use sabia_core::engine::{Engine, ScanOptions};
@@ -506,9 +506,9 @@ fn update_apply(state: tauri::State<'_, State>) -> Result<String, String> {
state.0.update_apply().map_err(|e| e.to_string())
}
/// `true` quando o processo já tem privilégio suficiente pra tudo que o
/// Sabiá faz (modo monitor, captura crua). No Windows o manifesto já
/// exige administrador antes mesmo do app abrir, então aqui é sempre `true`.
/// `true` when the process already has sufficient privileges for all
/// Sabiá operations (monitor mode, raw capture). On Windows the manifest already
/// requires administrator privileges before launch, so this is always `true`.
#[tauri::command]
fn is_elevated() -> bool {
#[cfg(unix)]
@@ -521,9 +521,9 @@ fn is_elevated() -> bool {
}
}
/// Reabre o app com privilégios de administrador (Linux/macOS). O app novo
/// encerra este quando abre; se a autorização falhar ou for cancelada, a
/// janela continua aqui e a interface mostra o motivo.
/// Relaunches the app with administrator privileges (Linux/macOS). The new instance
/// terminates this one on startup; if authorization fails or is cancelled, the
/// window stays open and the UI displays the reason.
#[tauri::command]
fn relaunch_elevated(app: tauri::AppHandle) -> Result<(), String> {
#[cfg(any(target_os = "linux", target_os = "macos"))]
@@ -610,9 +610,9 @@ fn do_relaunch_elevated(app: tauri::AppHandle) -> Result<(), String> {
Ok(())
}
/// Caminho que o root consegue executar. No AppImage, `current_exe` aponta
/// pra dentro de um ponto de montagem FUSE do usuário, inacessível ao root:
/// usa o próprio arquivo `.AppImage` (variável `APPIMAGE`).
/// Executable path accessible to root. Inside an AppImage, `current_exe` points
/// inside a user FUSE mount that root cannot access:
/// use the `.AppImage` file itself (`APPIMAGE` environment variable).
#[cfg(any(target_os = "linux", target_os = "macos"))]
fn elevated_exe() -> Result<std::path::PathBuf, String> {
if let Some(p) = std::env::var_os("APPIMAGE")
@@ -628,8 +628,11 @@ fn main() {
let engine = Arc::new(Engine::new());
tauri::Builder::default()
.manage(State(engine))
.setup(|app| {
// Quando abrimos elevados a pedido do app normal, ele fecha o app antigo.
.setup(|_app| {
#[cfg(any(target_os = "linux", target_os = "macos"))]
let app = _app;
// When launched elevated by the unprivileged instance, terminate the old process.
#[cfg(unix)]
if let Some(pid) = std::env::var("SABIA_REPLACE_PID")
.ok()
@@ -637,7 +640,7 @@ fn main() {
{
std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_millis(1200));
// SAFETY: só envia SIGTERM ao processo anterior do próprio usuário.
// SAFETY: only sends SIGTERM to the previous process owned by the invoking user.
unsafe {
if libc::geteuid() == 0 {
libc::kill(pid, libc::SIGTERM);
@@ -646,10 +649,10 @@ fn main() {
});
}
// Captura crua e modo monitor exigem root: pede elevação assim
// que o app abre, sem esperar o usuário procurar um botão. Se
// a autorização falhar, a janela continua aberta e a interface
// mostra o aviso (com um botão pra tentar de novo).
// Raw capture and monitor mode require root: request elevation as soon
// as the app launches, without waiting for the user to look for a button. If
// authorization fails, the window stays open and the UI
// displays a warning banner (with a retry button).
#[cfg(any(target_os = "linux", target_os = "macos"))]
if unsafe { libc::geteuid() != 0 } && std::env::var("SABIA_REPLACE_PID").is_err() {
let handle = app.handle().clone();
+13 -11
View File
@@ -1,4 +1,4 @@
// Dicionário português -> inglês da interface. {n} marca um valor variável.
// Portuguese -> English UI dictionary. {n} marks a variable placeholder.
export const EN = {
// index.html / sidebar
'auditoria de Wi-Fi': 'Wi-Fi auditing',
@@ -19,7 +19,7 @@ export const EN = {
'modo monitor': 'monitor mode',
'modo gerenciado': 'managed mode',
// elevação / avisos
// elevation / notices
'Pedindo privilégio de administrador — necessário pra modo monitor e captura…':
'Requesting administrator privilege — needed for monitor mode and capture…',
'tentar de novo': 'try again',
@@ -27,13 +27,13 @@ export const EN = {
'Use somente em redes próprias ou com autorização explícita do responsável. Toda ativação de modo monitor e sessão de captura fica registrada na trilha de auditoria local.':
'Use only on your own networks or with the explicit authorization of the responsible party. Every monitor mode activation and capture session is logged in the local audit trail.',
// controles
// controls
'ativar modo monitor': 'enable monitor mode',
'desativar modo monitor': 'disable monitor mode',
'iniciar captura': 'start capture',
'parar captura': 'stop capture',
// resultados
// results
'Redes': 'Networks',
'Redes vistas': 'Networks seen',
'Linha do tempo': 'Timeline',
@@ -72,7 +72,7 @@ export const EN = {
'Nenhuma rede corresponde ao filtro': 'No network matches the filter',
'Ajuste a busca ou limpe o campo de filtro.': 'Adjust the search or clear the filter.',
// erros de UI
// UI errors
'Não foi possível trocar o modo:': 'Could not change the mode:',
'Não foi possível iniciar a captura:': 'Could not start the capture:',
'Não foi possível retomar a captura: {n}': 'Could not resume the capture: {n}',
@@ -82,7 +82,7 @@ export const EN = {
'erro desconhecido': 'unknown error',
'rota desconhecida': 'unknown route',
// seletor de idioma / configurações
// language selector / settings
'Configurações': 'Settings',
'Tema': 'Theme',
'Claro': 'Light',
@@ -133,7 +133,7 @@ export const EN = {
'quantidade de quadros deve ficar entre 1 e 256': 'frame count must be between 1 and 256',
'MAC inválido: {n}': 'invalid MAC: {n}',
// detalhes do alvo e ataques
// target details and attacks
'não': 'no',
'não anunciado': 'not advertised',
'quebrável': 'crackable',
@@ -340,7 +340,7 @@ export const EN = {
'{n} · canal {n}': '{n} · channel {n}',
'{n} ({n})': '{n} ({n})',
// canais, PMKID/PMF, WPS e relatório
// channels, PMKID/PMF, WPS, and report
'Ocupação dos canais em {n}': 'Channel occupancy on {n}',
'Sem dados de canal ainda': 'No channel data yet',
'Inicie a captura para o Sabiá medir ocupação e sugerir canais.':
@@ -358,7 +358,7 @@ export const EN = {
'Melhor canal': 'Best channel',
'Espectro e canais': 'Spectrum and channels',
// saúde do espectro (canais, estilo SMART)
// spectrum health (channels, SMART style)
'Boa': 'Good',
'Atenção': 'Attention',
'Saturado': 'Saturated',
@@ -478,7 +478,7 @@ export const EN = {
'não foi possível executar {n}: {n}': 'could not run {n}: {n}',
'{n} terminou com código {n}': '{n} exited with code {n}',
// achados do WIDS (analysis.rs)
// WIDS findings (analysis.rs)
'SSID “{n}” com seguranças diferentes': 'SSID “{n}” with different security levels',
'O mesmo nome aparece em BSSIDs com níveis de segurança distintos: {n}. Pode ser um evil twin ou um AP mal configurado.':
'The same name appears on BSSIDs with different security levels: {n}. It may be an evil twin or a misconfigured AP.',
@@ -525,7 +525,7 @@ export const EN = {
'The station did not reassociate in the observed window. PMF may be active, or the client simply stayed idle.',
'Inconclusivo': 'Inconclusive',
// deauth contínuo e identificação de clientes
// continuous deauth and client identification
'Mantém os quadros saindo em série até você parar (ou até sair o handshake). O canal da captura fica travado no AP durante o job.':
'Keeps frames going until you stop (or a handshake appears). The capture channel stays locked to the AP during the job.',
'Iniciar contínuo': 'Start continuous',
@@ -547,6 +547,8 @@ export const EN = {
'The bundled reaver could not be extracted in this run.',
'Dicionário (vazio = embutido, 9,7 milhões de senhas)': 'Wordlist (empty = bundled, 9.7 million passwords)',
'dicionário próprio, opcional': 'custom wordlist, optional',
'Aplicar regras de mutação (maiúscula, sufixos, leet — multiplica o tempo)':
'Apply mutation rules (uppercase, suffixes, leetspeak — multiplies run time)',
'Dispositivo': 'Device',
'MAC aleatório': 'Random MAC',
'Cliente {n}': 'Client {n}',