4 Commits
Author SHA1 Message Date
Lucas RayanandClaude Sonnet 5.5 c7638a2f4e ci: build the desktop app (Linux and Windows) automatically on release
Release / Cria a release (push) Successful in 4s
Release / App desktop (linux) (push) Failing after 8m1s
Release / CLI (linux-aarch64) (push) Successful in 51s
Release / CLI (linux-x86_64) (push) Successful in 1m13s
Release / App desktop (windows) (push) Failing after 7m15s
Mirrors Guará's release workflow. No signing key needed here: Sabiá's updater
is the custom module in sabia-core, not the Tauri plugin.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 13:42:35 -03:00
Lucas RayanandClaude Sonnet 5.5 d885d92643 release 1.0.1: update from Gitea, Linux-only CLI, desktop app for Linux and Windows
CI / Formatação e clippy (push) Successful in 49s
CI / Testes (Linux) (push) Successful in 49s
CI / Auditoria de dependências (push) Successful in 12s
CI / Compila o app desktop (Linux) (push) Successful in 1m12s
The updater reads the latest release from Gitea. update::apply refuses to
overwrite anything but the CLI. Release workflow builds the Linux CLI;
scripts/build-gui.sh builds the Linux and Windows desktop app. Docs say Linux
and Windows only.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 13:17:32 -03:00
Lucas RayanandClaude Sonnet 5.5 0e31d28342 ci: create a Gitea release when a v* tag is pushed
CI / Formatação e clippy (push) Successful in 26s
CI / Testes (Linux) (push) Successful in 22s
CI / Compila o app desktop (Linux) (push) Successful in 4m23s
CI / Auditoria de dependências (push) Successful in 5m31s
Notes come from the matching CHANGELOG.md section. The macOS and Windows
binaries the old release workflow built need runners this Gitea does not have.
The CI also ignores .wolf, .claude and the release files.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 12:37:01 -03:00
Lucas RayanandClaude Sonnet 5.5 9bcdd6c139 ci: move the CI to Gitea Actions and cache what is rebuilt
CI / Testes (Linux) (push) Successful in 1m1s
CI / Formatação e clippy (push) Successful in 1m43s
CI / Auditoria de dependências (push) Successful in 5m19s
CI / Compila o app desktop (Linux) (push) Successful in 4m59s
Linux-only runner, so the Windows and macOS legs and GitHub's audit action are
gone. The Rust toolchain, cargo registry and target directories live on the
runner's persistent toolcache volume. release.yml stays under .github and is
not run by Gitea.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-30 10:45:46 -03:00
19 changed files with 405 additions and 56 deletions

No files matched your search

@@ -4,9 +4,9 @@ on:
# Mudança só de documentação não precisa de 6 jobs em 3 sistemas. # Mudança só de documentação não precisa de 6 jobs em 3 sistemas.
push: push:
branches: [main] branches: [main]
paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**'] paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**', '.wolf/**', '.claude/**', 'scripts/release-notes.sh', '.gitea/workflows/release.yml']
pull_request: pull_request:
paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**'] paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**', '.wolf/**', '.claude/**', 'scripts/release-notes.sh', '.gitea/workflows/release.yml']
concurrency: concurrency:
group: ci-${{ github.ref }} group: ci-${{ github.ref }}
@@ -15,17 +15,24 @@ concurrency:
env: env:
CARGO_TERM_COLOR: always CARGO_TERM_COLOR: always
RUSTFLAGS: -D warnings RUSTFLAGS: -D warnings
# O runner mantém /opt/hostedtoolcache num volume que sobrevive entre jobs:
# a toolchain do rustup, o registry do cargo e o diretório target ficam ali,
# então nada disso é baixado ou recompilado do zero a cada execução. Cada job
# usa o seu próprio target (flags diferentes invalidariam o de outro job).
CARGO_HOME: /opt/hostedtoolcache/cargo
RUSTUP_HOME: /opt/hostedtoolcache/rustup
jobs: jobs:
lint: lint:
name: Formatação e clippy name: Formatação e clippy
runs-on: ubuntu-24.04 runs-on: ubuntu-latest
env:
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-lint
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
with: with:
components: rustfmt, clippy components: rustfmt, clippy
- uses: Swatinem/rust-cache@v2
- uses: actions/setup-node@v7 - uses: actions/setup-node@v7
with: with:
node-version: 22 node-version: 22
@@ -34,37 +41,39 @@ jobs:
- run: cargo fmt --all -- --check - run: cargo fmt --all -- --check
- run: cargo clippy --workspace --all-targets - run: cargo clippy --workspace --all-targets
# Windows e macOS saem: o Gitea aqui tem um único runner, Linux arm64.
test: test:
name: Testes (${{ matrix.os }}) name: Testes (Linux)
runs-on: ${{ matrix.os }} runs-on: ubuntu-latest
strategy: env:
fail-fast: false CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-test
matrix:
os: [ubuntu-24.04, windows-latest, macos-latest]
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo test --workspace - run: cargo test --workspace
audit: audit:
name: Auditoria de dependências name: Auditoria de dependências
runs-on: ubuntu-24.04 runs-on: ubuntu-latest
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- uses: rustsec/audit-check@v2 - uses: dtolnay/rust-toolchain@stable
with: # rustsec/audit-check depends on GitHub's checks API. cargo-audit itself
token: ${{ secrets.GITHUB_TOKEN }} # needs nothing but the advisory database, and installing it once into the
# persistent CARGO_HOME makes every later run skip the compile.
- name: cargo audit
run: |
command -v cargo-audit >/dev/null || cargo install cargo-audit --locked
cargo audit
gui: gui:
name: Compila o app desktop (Linux) name: Compila o app desktop (Linux)
runs-on: ubuntu-24.04 runs-on: ubuntu-latest
env:
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-gui
steps: steps:
- uses: actions/checkout@v7 - uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable - uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: apps/gui/src-tauri
- uses: actions/setup-node@v7 - uses: actions/setup-node@v7
with: with:
node-version: 22 node-version: 22
+106
View File
@@ -0,0 +1,106 @@
name: Release
# Quando uma tag v* é enviada: cria a entrada em "Releases" com as notas do
# CHANGELOG.md e anexa o CLI compilado (scripts/package-cli.sh), com um .sha256 ao
# lado de cada pacote. É o que o atualizador (`sabia update`) baixa.
#
# O CLI é só para Linux (x86_64 e aarch64), compilado no runner Linux; o x86_64
# por compilação cruzada. Windows e macOS têm só o app desktop.
on:
push:
tags: ['v*']
env:
CARGO_TERM_COLOR: always
# Toolchain e registry do cargo ficam no volume que sobrevive entre jobs.
CARGO_HOME: /opt/hostedtoolcache/cargo
RUSTUP_HOME: /opt/hostedtoolcache/rustup
jobs:
release:
name: Cria a release
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Release
env:
TAG: ${{ gitea.ref_name }}
TOKEN: ${{ gitea.token }}
API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}
run: |
# Uma execução repetida não recria a release.
if curl -fsS -H "Authorization: token $TOKEN" "$API/releases/tags/$TAG" >/dev/null 2>&1; then
echo "a release $TAG já existe"; exit 0
fi
scripts/release-notes.sh "$TAG" > notes.md
pre=false; case "$TAG" in *-*) pre=true ;; esac
jq -n --arg tag "$TAG" --arg name "Sabiá ${TAG#v}" --rawfile body notes.md --argjson pre "$pre" \
'{tag_name:$tag, name:$name, body:$body, prerelease:$pre}' > release.json
curl -fsS -X POST "$API/releases" -H "Authorization: token $TOKEN" \
-H 'Content-Type: application/json' -d @release.json >/dev/null
cli:
name: CLI (${{ matrix.platform }})
needs: release
runs-on: ubuntu-latest
env:
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-release
strategy:
fail-fast: false
matrix:
include:
- { platform: linux-aarch64, target: aarch64-unknown-linux-gnu, apt: "" }
- { platform: linux-x86_64, target: x86_64-unknown-linux-gnu, apt: "gcc-x86-64-linux-gnu" }
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Linker e ferramentas desta plataforma
if: ${{ matrix.apt != '' }}
run: |
apt-get update -qq
apt-get install -y -qq ${{ matrix.apt }}
- name: Compila e empacota
run: scripts/package-cli.sh "${{ gitea.ref_name }}" "${{ matrix.platform }}" "${{ matrix.target }}" | tee packages.txt
- name: Anexa à release
env:
TOKEN: ${{ gitea.token }}
API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}
run: scripts/release-upload.sh "${{ gitea.ref_name }}" $(cat packages.txt)
gui:
name: App desktop (${{ matrix.platform }})
needs: release
runs-on: ubuntu-latest
env:
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-release-gui
strategy:
fail-fast: false
matrix:
platform: [linux, windows]
steps:
- uses: actions/checkout@v7
- uses: dtolnay/rust-toolchain@stable
- uses: actions/setup-node@v7
with:
node-version: 22
- name: Dependências do Tauri (Linux)
if: matrix.platform == 'linux'
timeout-minutes: 25
run: scripts/apt-install.sh ~/apt-cache libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
- name: Ferramentas para compilação cruzada (Windows)
if: matrix.platform == 'windows'
run: |
apt-get update -qq
apt-get install -y -qq nsis lld llvm clang
command -v cargo-xwin >/dev/null || cargo install cargo-xwin --locked
- name: Compila e empacota
run: scripts/build-gui.sh "${{ matrix.platform }}"
- name: Anexa à release
env:
TOKEN: ${{ gitea.token }}
API: ${{ gitea.server_url }}/api/v1/repos/${{ gitea.repository }}
run: scripts/release-upload.sh "${{ gitea.ref_name }}" dist-gui/*
+4
View File
@@ -18,3 +18,7 @@ apps/gui/src-tauri/target
CLAUDE.md CLAUDE.md
AGENTS.md AGENTS.md
GEMINI.md GEMINI.md
# Pacotes gerados por scripts/package-cli.sh
/sabia-v*
/dist-gui
+21
View File
@@ -4,6 +4,27 @@
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow SemVer. Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow SemVer.
## [1.0.1] - 2026-10-30
The project moved from GitHub to Gitea, and the update check followed it.
### Changed
- **Updates now come from Gitea.** `sabia update` and the desktop app's "Update now"
read the latest release from `git.lucasrguerra.dev.br` instead of the GitHub API.
Versions up to 1.0.0 still look at GitHub, which is no longer reachable, so they
cannot find this release: download 1.0.1 once, and later updates are automatic.
- Release packages: the CLI for Linux x86_64 and aarch64 (built by the Gitea CI when
a `v*` tag is pushed, with a `.sha256` next to each package), and the desktop app
for Linux (`.AppImage`, `.deb`) and Windows (NSIS `.exe` installer, cross-compiled
from Linux by `scripts/build-gui.sh`). **There is no Windows CLI and no macOS build
any more**: the CLI is Linux-only, and macOS needs Apple's SDK and code signing,
which this project's CI cannot provide.
### Fixed
- `sabia update` refuses to run from the desktop app's executable (`sabia-gui`).
The release package contains the CLI, and replacing the app with it would have
broken the app; the app now points to the downloads page instead.
## [1.0.0] - 2026-10-24 ## [1.0.0] - 2026-10-24
First stable release: real-hardware validation across the whole attack surface, a much more complete cracking pipeline, two new attacks (WPS, Evil Twin), a redesigned report, and several reliability fixes found only by testing against live Wi-Fi networks. First stable release: real-hardware validation across the whole attack surface, a much more complete cracking pipeline, two new attacks (WPS, Evil Twin), a redesigned report, and several reliability fixes found only by testing against live Wi-Fi networks.
+21
View File
@@ -4,6 +4,27 @@
Formato baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/); versões seguem SemVer. Formato baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/); versões seguem SemVer.
## [1.0.1] - 2026-10-30
O projeto saiu do GitHub para o Gitea, e a verificação de atualização foi junto.
### Alterado
- **As atualizações agora vêm do Gitea.** O `sabia update` e o "Atualizar agora" do app
desktop leem a última release em `git.lucasrguerra.dev.br`, e não mais a API do
GitHub. As versões até a 1.0.0 ainda olham o GitHub, que não responde mais, e por isso
não enxergam esta release: baixe a 1.0.1 uma vez, e as próximas atualizações são automáticas.
- Pacotes da release: o CLI para Linux x86_64 e aarch64 (compilado pelo CI do Gitea
quando uma tag `v*` é enviada, com um `.sha256` ao lado de cada pacote) e o app desktop
para Linux (`.AppImage`, `.deb`) e Windows (instalador NSIS `.exe`, compilado a partir
do Linux pelo `scripts/build-gui.sh`). **Não há mais CLI para Windows nem build para
macOS**: o CLI é só para Linux, e o macOS exige o SDK e a assinatura da Apple, que o CI
deste projeto não tem.
### Corrigido
- O `sabia update` se recusa a rodar a partir do executável do app desktop (`sabia-gui`).
O pacote da release contém o CLI, e trocar o app por ele o quebraria; o app passa a
apontar para a página de downloads.
## [1.0.0] - 2026-10-24 ## [1.0.0] - 2026-10-24
Primeira versão estável: validação em hardware real por toda a superfície de ataque, um pipeline de quebra bem mais completo, dois ataques novos (WPS, Evil Twin), relatório redesenhado e várias correções de confiabilidade que só apareceram testando contra redes Wi-Fi de verdade. Primeira versão estável: validação em hardware real por toda a superfície de ataque, um pipeline de quebra bem mais completo, dois ataques novos (WPS, Evil Twin), relatório redesenhado e várias correções de confiabilidade que só apareceram testando contra redes Wi-Fi de verdade.
+2 -2
View File
@@ -17,9 +17,9 @@ cargo test --workspace
node scripts/check-ui-i18n.mjs # UI translation coverage node scripts/check-ui-i18n.mjs # UI translation coverage
``` ```
CI runs on Linux, Windows and macOS; run these before pushing — almost everything that fails CI shows up here in seconds. Documentation-only changes (`*.md`, `docs/`) are light on CI. CI runs on Linux only (Gitea has one runner); run these before pushing — almost everything that fails CI shows up here in seconds. Documentation-only changes (`*.md`, `docs/`) are light on CI.
Live capture exists only on Linux (`AF_PACKET` + netlink); on Windows and macOS those paths compile but aren't exercised. Parsing and cracking tests run on all three, because they're offline. Live capture exists only on Linux (`AF_PACKET` + netlink); the code behind Windows and macOS paths still compiles and is covered by `cargo check`, but neither runs in CI nor is built for release.
## Where to work ## Where to work
+2 -2
View File
@@ -17,9 +17,9 @@ cargo test --workspace
node scripts/check-ui-i18n.mjs # cobertura da tradução da interface node scripts/check-ui-i18n.mjs # cobertura da tradução da interface
``` ```
O CI roda em Linux, Windows e macOS; rode esses comandos antes do push — quase tudo que faz o CI falhar aparece aqui em segundos. Mudanças só de documentação (`*.md`, `docs/`) são leves no CI. O CI roda só em Linux (o Gitea tem um único runner); rode esses comandos antes do push — quase tudo que faz o CI falhar aparece aqui em segundos. Mudanças só de documentação (`*.md`, `docs/`) são leves no CI.
A captura ao vivo só existe no Linux (`AF_PACKET` + netlink); em Windows e macOS esses caminhos são compilados mas não exercitados. Testes de parsing e de quebra rodam nas três plataformas, porque são offline. A captura ao vivo só existe no Linux (`AF_PACKET` + netlink); o código dos caminhos de Windows e macOS continua compilando e é coberto pelo `cargo check`, mas não roda no CI nem é compilado para release. são offline.
## Onde mexer ## Onde mexer
Generated
+2 -2
View File
@@ -440,7 +440,7 @@ dependencies = [
[[package]] [[package]]
name = "sabia-cli" name = "sabia-cli"
version = "1.0.0" version = "1.0.1"
dependencies = [ dependencies = [
"clap", "clap",
"ctrlc", "ctrlc",
@@ -451,7 +451,7 @@ dependencies = [
[[package]] [[package]]
name = "sabia-core" name = "sabia-core"
version = "1.0.0" version = "1.0.1"
dependencies = [ dependencies = [
"aes", "aes",
"flate2", "flate2",
+2 -2
View File
@@ -3,10 +3,10 @@ resolver = "2"
members = ["crates/sabia-core", "crates/sabia-cli"] members = ["crates/sabia-core", "crates/sabia-cli"]
[workspace.package] [workspace.package]
version = "1.0.0" version = "1.0.1"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/lucasrguerra/sabia" repository = "https://git.lucasrguerra.dev.br/lucasrguerra/sabia"
authors = ["Sabiá contributors"] authors = ["Sabiá contributors"]
rust-version = "1.80" rust-version = "1.80"
+11 -9
View File
@@ -1,6 +1,6 @@
<p align="center"><img src="apps/gui/ui/sabia.png" width="300" alt="Sabiá"></p> <p align="center"><img src="apps/gui/ui/sabia.png" width="300" alt="Sabiá"></p>
<h1 align="center">Sabiá</h1> <h1 align="center">Sabiá</h1>
<p align="center">Free and open Wi-Fi auditing.<br>Terminal and desktop app · live capture on Linux, offline analysis on Windows, Linux and macOS · authorized use.</p> <p align="center">Free and open Wi-Fi auditing.<br>Terminal and desktop app · live capture on Linux, offline analysis on Windows too · authorized use.</p>
<p align="center"><a href="docs/cli.md">Terminal</a> · <a href="docs/interface.md">Interface</a> · <a href="docs/architecture.md">Architecture</a> · <a href="ROADMAP.md">Roadmap</a> · <a href="CHANGELOG.md">Changelog</a></p> <p align="center"><a href="docs/cli.md">Terminal</a> · <a href="docs/interface.md">Interface</a> · <a href="docs/architecture.md">Architecture</a> · <a href="ROADMAP.md">Roadmap</a> · <a href="CHANGELOG.md">Changelog</a></p>
@@ -37,17 +37,18 @@ Download the package for your system from **[Releases](../../releases)**:
| | CLI | Desktop app | | | CLI | Desktop app |
|---|---|---| |---|---|---|
| Windows | `sabia-*-windows-x86_64.zip` | `.msi` / `.exe` installer | | Windows | — | `.exe` installer |
| macOS | `sabia-*-macos-{aarch64,x86_64}.tar.gz` | `.dmg` |
| Linux | `sabia-*-linux-{x86_64,aarch64}.tar.gz` | `.AppImage` / `.deb` | | Linux | `sabia-*-linux-{x86_64,aarch64}.tar.gz` | `.AppImage` / `.deb` |
Each file has a `.sha256` next to it. Build from source: `cargo build --release -p sabia-cli` (Rust 1.80+). The CLI is Linux-only; the desktop app ships for Linux and Windows. Each CLI
package has a `.sha256` next to it. Build from source: `cargo build --release
-p sabia-cli` (Rust 1.80+, Linux).
**Updates:** the desktop app checks GitHub on startup; when a new version exists, a button offers **Update now**: it downloads the right binary for your platform, verifies the **SHA-256** and swaps the running executable — no manual download. The check is a single request to the releases API and sends nothing of yours. **Updates:** the desktop app checks Gitea on startup; when a new version exists, a button offers **Update now**. The CLI updates the same way (`sabia update`); it downloads the right binary for your platform, verifies the **SHA-256** and swaps the running executable — no manual download. The check is a single request to the releases API and sends nothing of yours.
> The binaries are **not signed yet**: Windows SmartScreen and macOS Gatekeeper will warn. Check the SHA-256. > The binaries are **not signed yet**: Windows SmartScreen will warn. Check the SHA-256.
**Capture and monitor mode require Linux and root** (`CAP_NET_ADMIN`/`CAP_NET_RAW`): raw capture uses `AF_PACKET` and interface control uses netlink. **Offline `.pcap` analysis and cracking** also run on Windows and macOS. The desktop app elevates to administrator on its own at startup. **Capture and monitor mode require Linux and root** — the only platform the CLI ships for (`CAP_NET_ADMIN`/`CAP_NET_RAW`): raw capture uses `AF_PACKET` and interface control uses netlink. **Offline `.pcap` analysis and cracking** also run on Windows and macOS. The desktop app elevates to administrator on its own at startup.
## Getting started ## Getting started
@@ -74,11 +75,11 @@ Desktop app (Tauri) with an interface sidebar, a capture panel with an animated
## Known limits ## Known limits
- **Capture is Linux-only**, with a card that supports **monitor mode** (and injection, for `deauth`). On Windows and macOS Sabiá only does `.pcap` analysis and offline cracking. - **Capture is Linux-only**, with a card that supports **monitor mode** (and injection, for `deauth`); the CLI is not built for other platforms. On Windows Sabiá's desktop app only does `.pcap` analysis and offline cracking.
- **WPA3/SAE** does not fall to handshake dictionaries — cracking covers **WPA/WPA2-PSK** (4-way and PMKID). - **WPA3/SAE** does not fall to handshake dictionaries — cracking covers **WPA/WPA2-PSK** (4-way and PMKID).
- **5 and 6 GHz** depend on the card and the regulatory domain; some channels stay passive (no probing). - **5 and 6 GHz** depend on the card and the regulatory domain; some channels stay passive (no probing).
- **Evil Twin's self-signed certificate:** the Enterprise variant's TLS certificate isn't trusted by clients that validate the chain (common on updated corporate/MDM devices) — that's the correct defense working, not a bug. - **Evil Twin's self-signed certificate:** the Enterprise variant's TLS certificate isn't trusted by clients that validate the chain (common on updated corporate/MDM devices) — that's the correct defense working, not a bug.
- **Windows and macOS:** the code compiles and is tested in CI, but live capture does not run on those platforms. - **Windows:** the code compiles and is tested in CI, but live capture does not run there; the desktop app does offline analysis only. There is no macOS build: Tauri needs the macOS SDK and code signing, both tied to Apple hardware and an Apple Developer account, which this project's CI (a single Linux runner) does not have.
## Where it's going ## Where it's going
@@ -88,6 +89,7 @@ Summary of the [ROADMAP](ROADMAP.md):
|---|---| |---|---|
| **Done** | Monitor mode via netlink (with NetworkManager recovery), 2.4/5/6 GHz scanning, 4-way and PMKID capture, CCMP decryption, dictionary/rules/mask cracking (WPA/WPA2-PSK), `.hc22000` export, `.pcap` read/write, authorized `deauth`/PMF/PMKID, WPS attack, Evil Twin (PSK + Enterprise), channel scoring and findings, paginated audit report, hash-chained audit trail, `doctor`, CLI + desktop app, one-click update | | **Done** | Monitor mode via netlink (with NetworkManager recovery), 2.4/5/6 GHz scanning, 4-way and PMKID capture, CCMP decryption, dictionary/rules/mask cracking (WPA/WPA2-PSK), `.hc22000` export, `.pcap` read/write, authorized `deauth`/PMF/PMKID, WPS attack, Evil Twin (PSK + Enterprise), channel scoring and findings, paginated audit report, hash-chained audit trail, `doctor`, CLI + desktop app, one-click update |
| **Next** | GPU acceleration for cracking, capture on Windows (Npcap) | | **Next** | GPU acceleration for cracking, capture on Windows (Npcap) |
| **Not planned** | A macOS build — see [Known limits](#known-limits) |
| **Later** | WPA3/SAE downgrade, validation on varied hardware, signed release binaries | | **Later** | WPA3/SAE downgrade, validation on varied hardware, signed release binaries |
## Development ## Development
+11 -9
View File
@@ -1,6 +1,6 @@
<p align="center"><img src="apps/gui/ui/sabia.png" width="300" alt="Sabiá"></p> <p align="center"><img src="apps/gui/ui/sabia.png" width="300" alt="Sabiá"></p>
<h1 align="center">Sabiá</h1> <h1 align="center">Sabiá</h1>
<p align="center">Auditoria de Wi-Fi livre e gratuita.<br>Terminal e interface gráfica · captura no Linux, análise offline em Windows, Linux e macOS · uso autorizado.</p> <p align="center">Auditoria de Wi-Fi livre e gratuita.<br>Terminal e interface gráfica · captura no Linux, análise offline também em Windows · uso autorizado.</p>
<p align="center"><a href="docs/pt-BR/cli.md">Terminal</a> · <a href="docs/pt-BR/interface.md">Interface</a> · <a href="docs/pt-BR/arquitetura.md">Arquitetura</a> · <a href="ROADMAP.pt-BR.md">Roadmap</a> · <a href="CHANGELOG.pt-BR.md">Changelog</a></p> <p align="center"><a href="docs/pt-BR/cli.md">Terminal</a> · <a href="docs/pt-BR/interface.md">Interface</a> · <a href="docs/pt-BR/arquitetura.md">Arquitetura</a> · <a href="ROADMAP.pt-BR.md">Roadmap</a> · <a href="CHANGELOG.pt-BR.md">Changelog</a></p>
@@ -37,17 +37,18 @@ Baixe o pacote do seu sistema em **[Releases](../../releases)**:
| | CLI | App gráfico | | | CLI | App gráfico |
|---|---|---| |---|---|---|
| Windows | `sabia-*-windows-x86_64.zip` | instalador `.msi` / `.exe` | | Windows | — | instalador `.exe` |
| macOS | `sabia-*-macos-{aarch64,x86_64}.tar.gz` | `.dmg` |
| Linux | `sabia-*-linux-{x86_64,aarch64}.tar.gz` | `.AppImage` / `.deb` | | Linux | `sabia-*-linux-{x86_64,aarch64}.tar.gz` | `.AppImage` / `.deb` |
Cada arquivo tem um `.sha256` ao lado. Compilar do código: `cargo build --release -p sabia-cli` (Rust 1.80+). O CLI é só para Linux; o app desktop sai para Linux e Windows. Cada pacote do
CLI tem um `.sha256` ao lado. Compilar do código: `cargo build --release -p
sabia-cli` (Rust 1.80+, Linux).
**Atualizações:** o app desktop consulta o GitHub ao abrir; havendo versão nova, um botão oferece **Atualizar agora**: ele baixa o binário certo para a plataforma, confere o **SHA-256** e troca o executável em uso — você não precisa baixar nada na mão. A consulta é uma única requisição à API de releases e nada seu é enviado. **Atualizações:** o app desktop consulta o Gitea ao abrir; havendo versão nova, um botão oferece **Atualizar agora**. O CLI atualiza do mesmo jeito (`sabia update`); ele baixa o binário certo para a plataforma, confere o **SHA-256** e troca o executável em uso — você não precisa baixar nada na mão. A consulta é uma única requisição à API de releases e nada seu é enviado.
> Os binários ainda **não são assinados**: o SmartScreen (Windows) e o Gatekeeper (macOS) vão avisar. Confira o SHA-256. > Os binários ainda **não são assinados**: o SmartScreen do Windows vai avisar. Confira o SHA-256.
**Captura e modo monitor exigem Linux e privilégio de root** (`CAP_NET_ADMIN`/`CAP_NET_RAW`): a captura crua usa `AF_PACKET` e o controle de interface usa netlink. A **análise de `.pcap` e a quebra offline** rodam também em Windows e macOS. O app desktop pede elevação de administrador sozinho ao abrir. **Captura e modo monitor exigem Linux e privilégio de root** — a única plataforma para a qual o CLI é compilado (`CAP_NET_ADMIN`/`CAP_NET_RAW`): a captura crua usa `AF_PACKET` e o controle de interface usa netlink. A **análise de `.pcap` e a quebra offline** rodam também em Windows e macOS. O app desktop pede elevação de administrador sozinho ao abrir.
## Começando ## Começando
@@ -74,11 +75,11 @@ App desktop (Tauri) com sidebar de interfaces, painel de captura com linha de si
## Limites conhecidos ## Limites conhecidos
- **Captura só no Linux**, com placa que suporte **modo monitor** (e injeção, para `deauth`). Em Windows e macOS o Sabiá faz só análise de `.pcap` e quebra offline. - **Captura só no Linux**, com placa que suporte **modo monitor** (e injeção, para `deauth`); o CLI não é compilado para outras plataformas. Em Windows o app desktop do Sabiá faz só análise de `.pcap` e quebra offline.
- **WPA3/SAE** não cai por dicionário de handshake — a quebra cobre **WPA/WPA2-PSK** (4-way e PMKID). - **WPA3/SAE** não cai por dicionário de handshake — a quebra cobre **WPA/WPA2-PSK** (4-way e PMKID).
- **5 e 6 GHz** dependem da placa e do _regulatory domain_; alguns canais ficam passivos (sem _probe_). - **5 e 6 GHz** dependem da placa e do _regulatory domain_; alguns canais ficam passivos (sem _probe_).
- **Certificado autoassinado do Evil Twin Enterprise:** clientes que validam a cadeia de certificado da rede (comum em perfil corporativo/MDM atualizado) não confiam nele — é a defesa correta funcionando, não um bug. - **Certificado autoassinado do Evil Twin Enterprise:** clientes que validam a cadeia de certificado da rede (comum em perfil corporativo/MDM atualizado) não confiam nele — é a defesa correta funcionando, não um bug.
- **Windows e macOS:** o código compila e é testado em CI, mas a captura ao vivo não roda nessas plataformas. - **Windows:** o código compila e é testado em CI, mas a captura ao vivo não roda lá; o app desktop faz só análise offline. Não há build para macOS: o Tauri precisa do SDK do macOS e de assinatura, os dois presos a hardware Apple e a uma conta Apple Developer, que o CI deste projeto (um único runner Linux) não tem.
## Para onde vai ## Para onde vai
@@ -88,6 +89,7 @@ Resumo do [ROADMAP](ROADMAP.pt-BR.md):
|---|---| |---|---|
| **Feito** | Modo monitor por netlink (com recuperação do NetworkManager), varredura 2.4/5/6 GHz, captura de 4-way e PMKID, decriptação CCMP, quebra por dicionário/regras/máscara (WPA/WPA2-PSK), exportação `.hc22000`, escrita e leitura de `.pcap`, `deauth`/PMF/PMKID autorizados, ataque WPS, Evil Twin (PSK + Enterprise), pontuação de canais e achados, relatório de auditoria paginado, trilha de auditoria por hash, `doctor`, CLI + app desktop, atualização em um clique | | **Feito** | Modo monitor por netlink (com recuperação do NetworkManager), varredura 2.4/5/6 GHz, captura de 4-way e PMKID, decriptação CCMP, quebra por dicionário/regras/máscara (WPA/WPA2-PSK), exportação `.hc22000`, escrita e leitura de `.pcap`, `deauth`/PMF/PMKID autorizados, ataque WPS, Evil Twin (PSK + Enterprise), pontuação de canais e achados, relatório de auditoria paginado, trilha de auditoria por hash, `doctor`, CLI + app desktop, atualização em um clique |
| **Próximo** | Aceleração por GPU na quebra, captura em Windows (Npcap) | | **Próximo** | Aceleração por GPU na quebra, captura em Windows (Npcap) |
| **Fora de plano** | Um build para macOS — veja [Limites conhecidos](#limites-conhecidos) |
| **Depois** | WPA3/SAE _downgrade_, validação em hardware variado, binários de release assinados | | **Depois** | WPA3/SAE _downgrade_, validação em hardware variado, binários de release assinados |
## Desenvolvimento ## Desenvolvimento
+2 -2
View File
@@ -2538,7 +2538,7 @@ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
[[package]] [[package]]
name = "sabia-core" name = "sabia-core"
version = "1.0.0" version = "1.0.1"
dependencies = [ dependencies = [
"aes", "aes",
"flate2", "flate2",
@@ -2555,7 +2555,7 @@ dependencies = [
[[package]] [[package]]
name = "sabia-gui" name = "sabia-gui"
version = "1.0.0" version = "1.0.1"
dependencies = [ dependencies = [
"libc", "libc",
"sabia-core", "sabia-core",
+2 -2
View File
@@ -3,10 +3,10 @@
[package] [package]
name = "sabia-gui" name = "sabia-gui"
description = "Sabiá: interface gráfica de auditoria de Wi-Fi" description = "Sabiá: interface gráfica de auditoria de Wi-Fi"
version = "1.0.0" version = "1.0.1"
edition = "2021" edition = "2021"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
repository = "https://github.com/lucasrguerra/sabia" repository = "https://git.lucasrguerra.dev.br/lucasrguerra/sabia"
rust-version = "1.80" rust-version = "1.80"
[workspace] [workspace]
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"$schema": "https://schema.tauri.app/config/2", "$schema": "https://schema.tauri.app/config/2",
"productName": "Sabia", "productName": "Sabia",
"version": "1.0.0", "version": "1.0.1",
"identifier": "dev.sabia.app", "identifier": "dev.sabia.app",
"build": { "build": {
"frontendDist": "../ui" "frontendDist": "../ui"
+40 -6
View File
@@ -1,4 +1,4 @@
//! Auto-atualização simples: verifica a última release no GitHub, baixa o //! Auto-atualização simples: verifica a última release no Gitea, baixa o
//! binário certo para a plataforma atual, confere o checksum e troca o //! binário certo para a plataforma atual, confere o checksum e troca o
//! executável em uso — sem o usuário precisar baixar nada manualmente. //! executável em uso — sem o usuário precisar baixar nada manualmente.
//! //!
@@ -12,7 +12,13 @@ use std::fs;
use std::path::PathBuf; use std::path::PathBuf;
use std::process::Command; use std::process::Command;
const REPO: &str = "lucasrguerra/sabia"; /// A API do Gitea devolve o mesmo formato de `releases/latest` que o GitHub devolvia
/// (`tag_name`, `assets[].name`, `assets[].browser_download_url`).
const LATEST_RELEASE_URL: &str =
"https://git.lucasrguerra.dev.br/api/v1/repos/lucasrguerra/sabia/releases/latest";
/// Página de downloads, para quem usa o app desktop.
const RELEASES_PAGE_URL: &str = "https://git.lucasrguerra.dev.br/lucasrguerra/sabia/releases";
#[derive(Debug, Deserialize)] #[derive(Debug, Deserialize)]
struct Release { struct Release {
@@ -92,8 +98,8 @@ fn asset_suffix_for_platform() -> String {
/// Consulta a última release publicada e compara com a versão atual /// Consulta a última release publicada e compara com a versão atual
/// (`CARGO_PKG_VERSION` do binário chamador). /// (`CARGO_PKG_VERSION` do binário chamador).
pub fn check(current_version: &str) -> Result<UpdateStatus> { pub fn check(current_version: &str) -> Result<UpdateStatus> {
let url = format!("https://api.github.com/repos/{REPO}/releases/latest"); let url = LATEST_RELEASE_URL;
let body = match curl_text(&url) { let body = match curl_text(url) {
Ok(b) => b, Ok(b) => b,
Err(_) => { Err(_) => {
// Sem rede, ou repositório ainda sem releases publicadas: não é // Sem rede, ou repositório ainda sem releases publicadas: não é
@@ -118,8 +124,22 @@ pub fn check(current_version: &str) -> Result<UpdateStatus> {
/// Baixa e aplica a atualização mais recente, substituindo o binário atual. /// Baixa e aplica a atualização mais recente, substituindo o binário atual.
/// Retorna a versão instalada. /// Retorna a versão instalada.
pub fn apply(current_exe: &PathBuf) -> Result<String> { pub fn apply(current_exe: &PathBuf) -> Result<String> {
let url = format!("https://api.github.com/repos/{REPO}/releases/latest"); // O pacote da release contém o CLI (`sabia`). Trocar por ele um executável que
let body = curl_text(&url)?; // não seja o CLI, como o `sabia-gui`, o deixaria como um CLI e quebraria o app
// na próxima abertura. O app desktop se atualiza baixando o instalador.
let is_cli = current_exe
.file_stem()
.map(|stem| stem == "sabia")
.unwrap_or(false);
if !is_cli {
return Err(SabiaError::CommandFailed {
cmd: "update".into(),
detail: format!(
"a atualização automática vale só para o CLI `sabia`; para o app desktop, baixe o instalador em {RELEASES_PAGE_URL}"
),
});
}
let body = curl_text(LATEST_RELEASE_URL)?;
let release: Release = serde_json::from_str(&body)?; let release: Release = serde_json::from_str(&body)?;
let suffix = asset_suffix_for_platform(); let suffix = asset_suffix_for_platform();
@@ -225,3 +245,17 @@ fn find_binary(dir: &PathBuf) -> Result<PathBuf> {
detail: "binário não encontrado no pacote baixado".into(), detail: "binário não encontrado no pacote baixado".into(),
}) })
} }
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn apply_refuses_to_overwrite_the_desktop_app() {
// Falha antes de qualquer acesso à rede: o nome do executável já não é o do CLI.
let err = apply(&PathBuf::from("/opt/sabia/sabia-gui")).unwrap_err();
assert!(err.to_string().contains("só para o CLI"), "{err}");
let err = apply(&PathBuf::from("C:/Program Files/Sabia/sabia-gui.exe")).unwrap_err();
assert!(err.to_string().contains(RELEASES_PAGE_URL), "{err}");
}
}
+59
View File
@@ -0,0 +1,59 @@
#!/usr/bin/env bash
# Compila o app desktop (Tauri) e deixa os pacotes em dist-gui/, com os nomes
# que a release usa.
#
# scripts/build-gui.sh linux # AppImage e .deb; precisa de uma máquina Linux com os pacotes do Tauri
# scripts/build-gui.sh windows # instalador NSIS, compilado a partir do Linux
#
# O Windows usa cargo-xwin (SDK do Windows baixado na hora) e o NSIS, que o Tauri
# documenta como compilação cruzada experimental. Precisa de: nsis, lld, llvm,
# clang e `cargo install cargo-xwin`. O .msi não sai daqui (o WiX é só do Windows).
# `scripts/build-gui.sh windows-docker` roda o mesmo num container, sem instalar nada.
set -euo pipefail
cd "$(dirname "$0")/.."
platform=${1:?uso: build-gui.sh <linux|windows|windows-docker>}
tauri() { npx --yes @tauri-apps/cli@2 "$@"; }
if [ "$platform" = windows-docker ]; then
docker volume create sabia-cargo >/dev/null
docker volume create sabia-rustup >/dev/null
mkdir -p dist-gui
exec docker run --rm -v "$PWD":/src -v sabia-cargo:/root/.cargo -v sabia-rustup:/root/.rustup \
-w /src -e HOST_UID="$(id -u)" -e HOST_GID="$(id -g)" ubuntu:24.04 bash -c '
set -e
export DEBIAN_FRONTEND=noninteractive
apt-get update -qq
apt-get install -y -qq curl ca-certificates build-essential pkg-config nsis lld llvm clang nodejs npm unzip >/dev/null
[ -x /root/.cargo/bin/rustup ] || curl -fsSL https://sh.rustup.rs | sh -s -- -y --profile minimal >/dev/null
export PATH=/root/.cargo/bin:$PATH
command -v cargo-xwin >/dev/null || cargo install cargo-xwin --locked
scripts/build-gui.sh windows
chown -R "$HOST_UID:$HOST_GID" dist-gui apps/gui/src-tauri/target 2>/dev/null || true'
fi
mkdir -p dist-gui
cd apps/gui/src-tauri
# Os ícones derivados do original não ficam todos no repositório.
[ -f icons/icon.ico ] || tauri icon icons/source.png -o icons
case "$platform" in
linux)
tauri build --bundles appimage,deb
bundle=target/release/bundle
cp "$bundle"/appimage/*.AppImage "$bundle"/deb/*.deb ../../../dist-gui/
# O atualizador do Tauri (quando o projeto usa) precisa do .sig ao lado.
cp "$bundle"/appimage/*.AppImage.sig ../../../dist-gui/ 2>/dev/null || true
;;
windows)
rustup target add x86_64-pc-windows-msvc >/dev/null
tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles nsis
bundle=target/x86_64-pc-windows-msvc/release/bundle/nsis
cp "$bundle"/*-setup.exe ../../../dist-gui/
cp "$bundle"/*-setup.exe.sig ../../../dist-gui/ 2>/dev/null || true
;;
*) echo "plataforma desconhecida: $platform" >&2; exit 2 ;;
esac
ls -1 ../../../dist-gui
+39
View File
@@ -0,0 +1,39 @@
#!/usr/bin/env bash
# Compila o CLI para uma plataforma e empacota como o atualizador espera:
# sabia-<tag>-<plataforma>.tar.gz com um .sha256 ao lado.
#
# scripts/package-cli.sh v1.0.1 linux-x86_64 x86_64-unknown-linux-gnu
#
# Plataformas: linux-x86_64 e linux-aarch64. O alvo diferente da máquina de build
# usa o linker cruzado correspondente (gcc-x86-64-linux-gnu). Escreve os arquivos
# na raiz do repositório e imprime os nomes.
set -euo pipefail
cd "$(dirname "$0")/.."
tag=${1:?uso: package-cli.sh <tag> <plataforma> <alvo-rust>}
platform=${2:?uso: package-cli.sh <tag> <plataforma> <alvo-rust>}
target=${3:?uso: package-cli.sh <tag> <plataforma> <alvo-rust>}
case "$target" in
x86_64-unknown-linux-gnu)
if [ "$(uname -m)" != x86_64 ]; then
export CARGO_TARGET_X86_64_UNKNOWN_LINUX_GNU_LINKER=x86_64-linux-gnu-gcc
export CC_x86_64_unknown_linux_gnu=x86_64-linux-gnu-gcc
fi ;;
esac
rustup target add "$target" >/dev/null 2>&1 || true
cargo build --release --locked -p sabia-cli --target "$target"
name="sabia-${tag}-${platform}"
rm -rf "$name"; mkdir "$name"
cp "${CARGO_TARGET_DIR:-target}/${target}/release/sabia" "$name/"
cp README.md LICENSE-MIT LICENSE-APACHE "$name/"
tar czf "$name.tar.gz" "$name"
asset="$name.tar.gz"
rm -rf "$name"
sha256sum "$asset" > "$asset.sha256"
echo "$asset"
echo "$asset.sha256"
+28
View File
@@ -0,0 +1,28 @@
#!/usr/bin/env bash
# Imprime as notas de uma release: a seção do CHANGELOG.md daquela versão, ou o
# log de commits desde a tag anterior quando o changelog não tem a seção.
#
# scripts/release-notes.sh v1.2.3
set -euo pipefail
tag=${1:?uso: release-notes.sh <tag>}
version=${tag#v}
section=""
if [ -f CHANGELOG.md ]; then
section=$(awk -v head="## [$version]" '
index($0, head) == 1 { found = 1; next }
found && /^## \[/ { exit }
found && /^\[[^]]+\]: / { next }
found { print }
' CHANGELOG.md)
fi
if [ -n "$(printf '%s' "$section" | tr -d '[:space:]')" ]; then
printf '%s\n' "$section"
else
previous=$(git describe --tags --abbrev=0 "$tag^" 2>/dev/null || true)
echo "## Mudanças${previous:+ desde $previous}"
echo
git log --no-merges --format='- %s (%h)' "${previous:+$previous..}$tag"
fi
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Anexa arquivos a uma release do Gitea, trocando os de mesmo nome (assim uma
# execução repetida do CI não falha por anexo duplicado).
#
# API=https://git.exemplo.com/api/v1/repos/dono/repo TOKEN=... \
# scripts/release-upload.sh v1.2.3 arquivo1 arquivo2
set -euo pipefail
: "${API:?defina API (URL do repositório na API do Gitea)}"
: "${TOKEN:?defina TOKEN}"
tag=${1:?uso: release-upload.sh <tag> <arquivo>...}
shift
auth=(-H "Authorization: token $TOKEN")
id=$(curl -fsS "${auth[@]}" "$API/releases/tags/$tag" | jq -r .id)
for file in "$@"; do
name=$(basename "$file")
for old in $(curl -fsS "${auth[@]}" "$API/releases/$id/assets" | jq -r --arg n "$name" '.[] | select(.name == $n) | .id'); do
curl -fsS -X DELETE "${auth[@]}" "$API/releases/$id/assets/$old"
done
curl -fsS -X POST "${auth[@]}" -F "attachment=@$file" "$API/releases/$id/assets?name=$name" >/dev/null
echo "enviado: $name"
done