Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
659701b2d4 | ||
|
|
30a086a808 | ||
|
|
833abc6308 | ||
|
|
3fc3febff3 | ||
|
|
827ef0c2c0 | ||
|
|
c7638a2f4e | ||
|
|
d885d92643 | ||
|
|
0e31d28342 | ||
|
|
9bcdd6c139 |
No files matched your search
@@ -0,0 +1,88 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
# Mudança só de documentação ou estado de ferramentas não precisa dos jobs de CI.
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**', '.wolf/**', '.claude/**', '.opencode/**', '.codex/**', '.cursor/**', '.superpowers/**', '.superset/**', '.gitea/ISSUE_TEMPLATE/**', 'scripts/release-notes.sh', 'scripts/release-upload.sh', '.gitea/workflows/release.yml']
|
||||
pull_request:
|
||||
paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**', '.wolf/**', '.claude/**', '.opencode/**', '.codex/**', '.cursor/**', '.superpowers/**', '.superset/**', '.gitea/ISSUE_TEMPLATE/**', 'scripts/release-notes.sh', 'scripts/release-upload.sh', '.gitea/workflows/release.yml']
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ gitea.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTFLAGS: -D warnings
|
||||
# O runner mantém /opt/hostedtoolcache num volume que sobrevive entre jobs:
|
||||
# a toolchain do rustup, o registry do cargo, caches de npm/apt e o diretório
|
||||
# target ficam ali, então nada disso é baixado ou recompilado do zero. 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
|
||||
NPM_CONFIG_CACHE: /opt/hostedtoolcache/npm-cache
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Formatação e clippy
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-lint
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Cobertura da tradução da interface
|
||||
run: node scripts/check-ui-i18n.mjs
|
||||
- run: cargo fmt --all -- --check
|
||||
- run: cargo clippy --workspace --all-targets
|
||||
|
||||
# Windows e macOS saem: o Gitea aqui tem um único runner, Linux arm64.
|
||||
test:
|
||||
name: Testes (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-test
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- run: cargo test --workspace
|
||||
|
||||
audit:
|
||||
name: Auditoria de dependências
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
# rustsec/audit-check depends on GitHub's checks API. cargo-audit itself
|
||||
# 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:
|
||||
name: Compila o app desktop (Linux)
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
CARGO_TARGET_DIR: /opt/hostedtoolcache/cargo-target/sabia-gui
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Dependências do Tauri
|
||||
timeout-minutes: 25
|
||||
run: scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/tauri-native libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
- name: Gera os ícones
|
||||
run: npx --yes @tauri-apps/cli@2 icon apps/gui/src-tauri/icons/source.png -o apps/gui/src-tauri/icons
|
||||
- run: cargo check --manifest-path apps/gui/src-tauri/Cargo.toml
|
||||
env:
|
||||
RUSTFLAGS: ""
|
||||
@@ -0,0 +1,131 @@
|
||||
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.
|
||||
#
|
||||
# Assinatura do atualizador (opcional): SABIA_TAURI_SIGNING_PRIVATE_KEY e
|
||||
# SABIA_TAURI_SIGNING_PRIVATE_KEY_PASSWORD, com os nomes antigos sem prefixo como
|
||||
# fallback. Sem chave, o build continua e apenas não gera .sig.
|
||||
on:
|
||||
push:
|
||||
tags: ['v*']
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
# Toolchain, caches e registry do cargo ficam no volume que sobrevive entre jobs.
|
||||
CARGO_HOME: /opt/hostedtoolcache/cargo
|
||||
RUSTUP_HOME: /opt/hostedtoolcache/rustup
|
||||
NPM_CONFIG_CACHE: /opt/hostedtoolcache/npm-cache
|
||||
XWIN_CACHE_DIR: /opt/hostedtoolcache/xwin-cache
|
||||
|
||||
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-cli-${{ matrix.platform }}
|
||||
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: scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/cli-x86_64 ${{ 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-${{ matrix.platform }}
|
||||
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.SABIA_TAURI_SIGNING_PRIVATE_KEY || secrets.TAURI_SIGNING_PRIVATE_KEY }}
|
||||
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.SABIA_TAURI_SIGNING_PRIVATE_KEY_PASSWORD || secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [linux-aarch64, linux-x86_64, 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 aarch64)
|
||||
if: matrix.platform == 'linux-aarch64'
|
||||
timeout-minutes: 25
|
||||
run: scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/tauri-native libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
- name: Dependências do Tauri (Linux x86_64)
|
||||
if: matrix.platform == 'linux-x86_64'
|
||||
timeout-minutes: 25
|
||||
run: |
|
||||
if [ "$(uname -m)" = aarch64 ]; then
|
||||
dpkg --add-architecture amd64
|
||||
. /etc/os-release
|
||||
if [ "${ID:-}" = ubuntu ]; then
|
||||
if [ -f /etc/apt/sources.list.d/ubuntu.sources ]; then
|
||||
grep -q '^Architectures:' /etc/apt/sources.list.d/ubuntu.sources || sed -i '/^Types: deb/a Architectures: arm64' /etc/apt/sources.list.d/ubuntu.sources
|
||||
printf 'Types: deb\nURIs: http://archive.ubuntu.com/ubuntu/\nSuites: %s %s-updates %s-security\nComponents: main universe\nArchitectures: amd64\nSigned-By: /usr/share/keyrings/ubuntu-archive-keyring.gpg\n' "$VERSION_CODENAME" "$VERSION_CODENAME" "$VERSION_CODENAME" >/etc/apt/sources.list.d/amd64.sources
|
||||
elif [ -f /etc/apt/sources.list ]; then
|
||||
sed -i 's/^deb http/deb [arch=arm64] http/' /etc/apt/sources.list
|
||||
printf 'deb [arch=amd64] http://archive.ubuntu.com/ubuntu/ %s main universe\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ %s-updates main universe\ndeb [arch=amd64] http://archive.ubuntu.com/ubuntu/ %s-security main universe\n' "$VERSION_CODENAME" "$VERSION_CODENAME" "$VERSION_CODENAME" >/etc/apt/sources.list.d/amd64.list
|
||||
fi
|
||||
fi
|
||||
scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/tauri-amd64 gcc-x86-64-linux-gnu g++-x86-64-linux-gnu pkg-config patchelf file squashfs-tools qemu-user libglib2.0-bin libwebkit2gtk-4.1-dev:amd64 libayatana-appindicator3-dev:amd64 librsvg2-dev:amd64
|
||||
else
|
||||
scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/tauri-native libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
fi
|
||||
- name: Ferramentas para compilação cruzada (Windows)
|
||||
if: matrix.platform == 'windows'
|
||||
run: |
|
||||
scripts/apt-install.sh /opt/hostedtoolcache/apt-cache/tauri-win 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/*
|
||||
@@ -0,0 +1,3 @@
|
||||
#!/usr/bin/env bash
|
||||
# Antes de cada push: a verificação rápida. Ignore com `git push --no-verify` (use com parcimônia).
|
||||
exec "$(git rev-parse --show-toplevel)/scripts/verificar.sh"
|
||||
@@ -1,83 +0,0 @@
|
||||
name: CI
|
||||
|
||||
on:
|
||||
# Mudança só de documentação não precisa de 6 jobs em 3 sistemas.
|
||||
push:
|
||||
branches: [main]
|
||||
paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**']
|
||||
pull_request:
|
||||
paths-ignore: ['**.md', 'docs/**', 'LICENSE*', '.gitignore', '.githooks/**']
|
||||
|
||||
concurrency:
|
||||
group: ci-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
RUSTFLAGS: -D warnings
|
||||
|
||||
jobs:
|
||||
lint:
|
||||
name: Formatação e clippy
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Cobertura da tradução da interface
|
||||
run: node scripts/check-ui-i18n.mjs
|
||||
- run: cargo fmt --all -- --check
|
||||
- run: cargo clippy --workspace --all-targets
|
||||
|
||||
test:
|
||||
name: Testes (${{ matrix.os }})
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os: [ubuntu-24.04, windows-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo test --workspace
|
||||
|
||||
audit:
|
||||
name: Auditoria de dependências
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: rustsec/audit-check@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
gui:
|
||||
name: Compila o app desktop (Linux)
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: apps/gui/src-tauri
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Cache dos pacotes do Tauri (.deb)
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/apt-cache
|
||||
key: apt-tauri-${{ runner.os }}-${{ runner.arch }}-${{ env.ImageVersion }}-${{ hashFiles('scripts/apt-install.sh') }}
|
||||
- name: Dependências do Tauri
|
||||
timeout-minutes: 25
|
||||
run: scripts/apt-install.sh ~/apt-cache libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
- name: Gera os ícones
|
||||
run: npx --yes @tauri-apps/cli@2 icon apps/gui/src-tauri/icons/source.png -o apps/gui/src-tauri/icons
|
||||
- run: cargo check --manifest-path apps/gui/src-tauri/Cargo.toml
|
||||
env:
|
||||
RUSTFLAGS: ""
|
||||
@@ -1,151 +0,0 @@
|
||||
name: Release
|
||||
|
||||
# Publica uma release pública ao empurrar uma tag vX.Y.Z (ex.: git tag v0.1.0 && git push --tags)
|
||||
on:
|
||||
push:
|
||||
tags: ["v*.*.*"]
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
env:
|
||||
CARGO_TERM_COLOR: always
|
||||
|
||||
jobs:
|
||||
create-release:
|
||||
name: Cria a release
|
||||
runs-on: ubuntu-24.04
|
||||
outputs:
|
||||
release_id: ${{ steps.rel.outputs.result }}
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Notas da versão (do CHANGELOG)
|
||||
id: notes
|
||||
run: |
|
||||
v="${GITHUB_REF_NAME#v}"
|
||||
v="${v%%-*}"
|
||||
awk -v v="$v" '$0 ~ "^## \\[" v "\\]" {f=1; next} /^## \[/ {f=0} f' CHANGELOG.md > notes.md
|
||||
[ -s notes.md ] || echo "Veja o CHANGELOG." > notes.md
|
||||
- uses: actions/github-script@v9
|
||||
id: rel
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const tag = context.ref.replace('refs/tags/', '');
|
||||
const r = await github.rest.repos.createRelease({
|
||||
owner: context.repo.owner, repo: context.repo.repo, tag_name: tag,
|
||||
name: `Sabiá ${tag}`, body: fs.readFileSync('notes.md', 'utf8'),
|
||||
draft: true, prerelease: tag.includes('-'),
|
||||
});
|
||||
return r.data.id;
|
||||
|
||||
cli:
|
||||
name: CLI (${{ matrix.name }})
|
||||
needs: create-release
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { name: linux-x86_64, os: ubuntu-24.04, target: x86_64-unknown-linux-gnu }
|
||||
- { name: linux-aarch64, os: ubuntu-24.04-arm, target: aarch64-unknown-linux-gnu }
|
||||
- { name: macos-aarch64, os: macos-latest, target: aarch64-apple-darwin }
|
||||
- { name: macos-x86_64, os: macos-latest, target: x86_64-apple-darwin }
|
||||
- { name: windows-x86_64, os: windows-latest, target: x86_64-pc-windows-msvc }
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
- run: cargo build --release --locked -p sabia-cli --target ${{ matrix.target }}
|
||||
- name: Empacota
|
||||
shell: bash
|
||||
run: |
|
||||
name="sabia-${GITHUB_REF_NAME}-${{ matrix.name }}"
|
||||
mkdir "$name"
|
||||
if [ "$RUNNER_OS" = "Windows" ]; then
|
||||
cp target/${{ matrix.target }}/release/sabia.exe "$name/"
|
||||
else
|
||||
cp target/${{ matrix.target }}/release/sabia "$name/"
|
||||
fi
|
||||
cp README.md LICENSE-MIT LICENSE-APACHE "$name/"
|
||||
if [ "$RUNNER_OS" = "Windows" ]; then
|
||||
7z a "$name.zip" "$name" >/dev/null; asset="$name.zip"
|
||||
else
|
||||
tar czf "$name.tar.gz" "$name"; asset="$name.tar.gz"
|
||||
fi
|
||||
if command -v sha256sum >/dev/null; then sha256sum "$asset" > "$asset.sha256"; else shasum -a 256 "$asset" > "$asset.sha256"; fi
|
||||
echo "ASSET=$asset" >> "$GITHUB_ENV"
|
||||
- name: Envia para a release
|
||||
shell: bash
|
||||
env:
|
||||
GH_TOKEN: ${{ github.token }}
|
||||
run: gh release upload "$GITHUB_REF_NAME" "$ASSET" "$ASSET.sha256" --clobber
|
||||
|
||||
gui:
|
||||
name: App desktop (${{ matrix.name }})
|
||||
needs: create-release
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { name: linux-x86_64, os: ubuntu-24.04, args: "" }
|
||||
- { name: macos-aarch64, os: macos-latest, args: "--target aarch64-apple-darwin" }
|
||||
- { name: macos-x86_64, os: macos-latest, args: "--target x86_64-apple-darwin" }
|
||||
- { name: windows-x86_64, os: windows-latest, args: "" }
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ contains(matrix.args, 'apple') && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
|
||||
- uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: apps/gui/src-tauri
|
||||
- uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Cache dos pacotes do Tauri (.deb)
|
||||
if: runner.os == 'Linux'
|
||||
uses: actions/cache@v6
|
||||
with:
|
||||
path: ~/apt-cache
|
||||
key: apt-tauri-${{ runner.os }}-${{ runner.arch }}-${{ env.ImageVersion }}-${{ hashFiles('scripts/apt-install.sh') }}
|
||||
- name: Dependências do Tauri (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
timeout-minutes: 25
|
||||
run: scripts/apt-install.sh ~/apt-cache libwebkit2gtk-4.1-dev libayatana-appindicator3-dev librsvg2-dev patchelf
|
||||
- name: Gera os ícones
|
||||
run: npx --yes @tauri-apps/cli@2 icon apps/gui/src-tauri/icons/source.png -o apps/gui/src-tauri/icons
|
||||
- uses: tauri-apps/tauri-action@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
projectPath: apps/gui/src-tauri
|
||||
releaseId: ${{ needs.create-release.outputs.release_id }}
|
||||
args: ${{ matrix.args }}
|
||||
|
||||
publish:
|
||||
name: Publica a release
|
||||
needs: [create-release, cli, gui]
|
||||
runs-on: ubuntu-24.04
|
||||
steps:
|
||||
- uses: actions/checkout@v7
|
||||
- name: Notas da versão (do CHANGELOG)
|
||||
run: |
|
||||
v="${GITHUB_REF_NAME#v}"
|
||||
v="${v%%-*}"
|
||||
awk -v v="$v" '$0 ~ "^## \\[" v "\\]" {f=1; next} /^## \[/ {f=0} f' CHANGELOG.md > notes.md
|
||||
[ -s notes.md ] || echo "Veja o CHANGELOG." > notes.md
|
||||
- uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
await github.rest.repos.updateRelease({
|
||||
owner: context.repo.owner, repo: context.repo.repo,
|
||||
release_id: ${{ needs.create-release.outputs.release_id }},
|
||||
name: `Sabiá ${context.ref.replace('refs/tags/', '')}`,
|
||||
body: fs.readFileSync('notes.md', 'utf8'),
|
||||
draft: false,
|
||||
});
|
||||
@@ -1,13 +1,21 @@
|
||||
/target
|
||||
# Build
|
||||
target/
|
||||
apps/gui/src-tauri/target/
|
||||
node_modules/
|
||||
*.img
|
||||
*.guara-session.json
|
||||
.DS_Store
|
||||
**/*.rs.bk
|
||||
|
||||
# Capturas e estado local do Sabiá
|
||||
*.pcap
|
||||
*.cap
|
||||
.sabia-audit.log
|
||||
node_modules/
|
||||
apps/gui/src-tauri/target
|
||||
|
||||
# Configuração e memória local de agentes de IA — não faz parte do
|
||||
# produto, fica só na máquina de desenvolvimento.
|
||||
# Logo copiada para a interface pelo build.rs
|
||||
/apps/gui/ui/logo.png
|
||||
|
||||
# Configuração e memória local de agentes de IA — não faz parte do produto
|
||||
.claude/
|
||||
.wolf/
|
||||
.opencode/
|
||||
@@ -18,3 +26,17 @@ apps/gui/src-tauri/target
|
||||
CLAUDE.md
|
||||
AGENTS.md
|
||||
GEMINI.md
|
||||
|
||||
# Variáveis de ambiente locais (o setup do Superset copia da raiz)
|
||||
.env
|
||||
.env.*
|
||||
|
||||
# Lixo local
|
||||
*.eml
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
# Pacotes gerados por scripts/package-cli.sh
|
||||
/guara-v*
|
||||
/sabia-v*
|
||||
/dist-gui
|
||||
@@ -4,6 +4,44 @@
|
||||
|
||||
Format based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/); versions follow SemVer.
|
||||
|
||||
## [1.0.3] - 2026-10-30
|
||||
|
||||
### Fixed
|
||||
- `deauth_directional` had no `#[cfg(target_os = "linux")]` of its own; every
|
||||
non-Linux build failed with a duplicate-definition error and unresolved
|
||||
Linux-only imports. `v1.0.1` and `v1.0.2` never got a working Windows
|
||||
desktop app for this reason, and `v1.0.2`'s Linux package was mislabeled
|
||||
`1.0.1` (the version in `Cargo.toml` was not bumped). Use `v1.0.3`.
|
||||
|
||||
## [1.0.2] - 2026-10-30
|
||||
|
||||
### Fixed
|
||||
- The desktop app build on CI looked for its bundle under `target/`, but the
|
||||
runner's Cargo target directory is elsewhere and persists between runs; the
|
||||
Linux and Windows jobs failed before publishing anything. `v1.0.1`'s desktop
|
||||
app was never published for this reason — use `v1.0.2`.
|
||||
|
||||
## [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
|
||||
|
||||
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.
|
||||
|
||||
@@ -4,6 +4,44 @@
|
||||
|
||||
Formato baseado em [Keep a Changelog](https://keepachangelog.com/pt-BR/1.1.0/); versões seguem SemVer.
|
||||
|
||||
## [1.0.3] - 2026-10-30
|
||||
|
||||
### Corrigido
|
||||
- O `deauth_directional` não tinha o próprio `#[cfg(target_os = "linux")]`;
|
||||
todo build fora do Linux falhava com uma definição duplicada e imports só de
|
||||
Linux fora de escopo. Por isso a `v1.0.1` e a `v1.0.2` nunca tiveram um app
|
||||
desktop funcional para Windows, e o pacote Linux da `v1.0.2` saiu rotulado
|
||||
`1.0.1` (a versão no `Cargo.toml` não tinha sido atualizada). Use a `v1.0.3`.
|
||||
|
||||
## [1.0.2] - 2026-10-30
|
||||
|
||||
### Corrigido
|
||||
- O build do app desktop no CI procurava o pacote em `target/`, mas o
|
||||
diretório do Cargo no runner é outro e persiste entre execuções; os jobs de
|
||||
Linux e Windows falhavam antes de publicar qualquer coisa. Por isso o app
|
||||
desktop da `v1.0.1` nunca foi publicado — use a `v1.0.2`.
|
||||
|
||||
## [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
|
||||
|
||||
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.
|
||||
|
||||
@@ -11,15 +11,20 @@ Thanks for helping! Golden rules:
|
||||
## Workflow
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo clippy --workspace --all-targets # as in CI (RUSTFLAGS=-D warnings)
|
||||
cargo test --workspace
|
||||
node scripts/check-ui-i18n.mjs # UI translation coverage
|
||||
scripts/instalar-hooks.sh # once: runs the quick check before every push
|
||||
scripts/verificar.sh # fast (seconds): fmt, clippy as in CI, i18n, scripts, workflows
|
||||
scripts/verificar.sh --completo # adds cargo test --workspace and cargo audit
|
||||
```
|
||||
|
||||
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; run the check before pushing: almost everything that fails CI shows up here in seconds. Documentation-only changes (`*.md`, `docs/`) skip 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`.
|
||||
|
||||
## Releases
|
||||
|
||||
Pushing a `v*` tag makes the Gitea CI create the release (notes come from `CHANGELOG.md`, so update it first) and attach the Linux CLI (x86_64 and aarch64) and the desktop app for Linux (x86_64 and aarch64) and Windows.
|
||||
|
||||
There is no macOS build: it needs Apple's SDK and code signing, which a Linux runner cannot provide.
|
||||
|
||||
## Where to work
|
||||
|
||||
|
||||
@@ -11,15 +11,20 @@ Obrigado por ajudar! Regras de ouro:
|
||||
## Fluxo
|
||||
|
||||
```bash
|
||||
cargo fmt --all
|
||||
cargo clippy --workspace --all-targets # como no CI (RUSTFLAGS=-D warnings)
|
||||
cargo test --workspace
|
||||
node scripts/check-ui-i18n.mjs # cobertura da tradução da interface
|
||||
scripts/instalar-hooks.sh # uma vez: roda a verificação rápida antes de cada push
|
||||
scripts/verificar.sh # rápido (segundos): fmt, clippy como no CI, tradução, scripts, workflows
|
||||
scripts/verificar.sh --completo # inclui cargo test --workspace e cargo audit
|
||||
```
|
||||
|
||||
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 em Linux; rode a verificação antes do push: quase tudo que faz o CI falhar aparece aqui em segundos. Mudanças só de documentação (`*.md`, `docs/`) pulam o 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`.
|
||||
|
||||
## Releases
|
||||
|
||||
Enviar uma tag `v*` faz o CI do Gitea criar a release (as notas vêm do `CHANGELOG.md`, então atualize-o antes) e anexar o CLI para Linux (x86_64 e aarch64) e o app desktop para Linux (x86_64 e aarch64) e Windows.
|
||||
|
||||
Não há build para macOS: ele exige o SDK e a assinatura da Apple, que um runner Linux não tem.
|
||||
|
||||
## Onde mexer
|
||||
|
||||
|
||||
@@ -440,7 +440,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sabia-cli"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"ctrlc",
|
||||
@@ -451,7 +451,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sabia-core"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"flate2",
|
||||
|
||||
@@ -3,10 +3,10 @@ resolver = "2"
|
||||
members = ["crates/sabia-core", "crates/sabia-cli"]
|
||||
|
||||
[workspace.package]
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/lucasrguerra/sabia"
|
||||
repository = "https://git.lucasrguerra.dev.br/lucasrguerra/sabia"
|
||||
authors = ["Sabiá contributors"]
|
||||
rust-version = "1.80"
|
||||
|
||||
@@ -15,3 +15,7 @@ lto = "thin"
|
||||
codegen-units = 1
|
||||
opt-level = 3
|
||||
strip = true
|
||||
panic = "abort"
|
||||
|
||||
[profile.dev.package."*"]
|
||||
opt-level = 3
|
||||
@@ -1,8 +1,8 @@
|
||||
<p align="center"><img src="apps/gui/ui/sabia.png" width="300" alt="Sabiá"></p>
|
||||
<p align="center"><img src="logo.png" width="300" alt="Sabiá"></p>
|
||||
<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="docs/support.md">Support</a> · <a href="ROADMAP.md">Roadmap</a> · <a href="CHANGELOG.md">Changelog</a></p>
|
||||
|
||||
<p align="center"><b>English</b> · <a href="README.pt-BR.md">Português</a></p>
|
||||
|
||||
@@ -37,17 +37,18 @@ Download the package for your system from **[Releases](../../releases)**:
|
||||
|
||||
| | CLI | Desktop app |
|
||||
|---|---|---|
|
||||
| Windows | `sabia-*-windows-x86_64.zip` | `.msi` / `.exe` installer |
|
||||
| macOS | `sabia-*-macos-{aarch64,x86_64}.tar.gz` | `.dmg` |
|
||||
| Windows | — | `.exe` installer |
|
||||
| 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
|
||||
|
||||
@@ -74,11 +75,11 @@ Desktop app (Tauri) with an interface sidebar, a capture panel with an animated
|
||||
|
||||
## 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).
|
||||
- **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.
|
||||
- **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
|
||||
|
||||
@@ -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 |
|
||||
| **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 |
|
||||
|
||||
## Development
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<p align="center"><img src="apps/gui/ui/sabia.png" width="300" alt="Sabiá"></p>
|
||||
<p align="center"><img src="logo.png" width="300" alt="Sabiá"></p>
|
||||
<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="docs/pt-BR/suporte.md">Suporte</a> · <a href="ROADMAP.pt-BR.md">Roadmap</a> · <a href="CHANGELOG.pt-BR.md">Changelog</a></p>
|
||||
|
||||
<p align="center"><a href="README.md">English</a> · <b>Português</b></p>
|
||||
|
||||
@@ -37,17 +37,18 @@ Baixe o pacote do seu sistema em **[Releases](../../releases)**:
|
||||
|
||||
| | CLI | App gráfico |
|
||||
|---|---|---|
|
||||
| Windows | `sabia-*-windows-x86_64.zip` | instalador `.msi` / `.exe` |
|
||||
| macOS | `sabia-*-macos-{aarch64,x86_64}.tar.gz` | `.dmg` |
|
||||
| Windows | — | instalador `.exe` |
|
||||
| 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
|
||||
|
||||
@@ -74,11 +75,11 @@ App desktop (Tauri) com sidebar de interfaces, painel de captura com linha de si
|
||||
|
||||
## 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).
|
||||
- **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.
|
||||
- **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
|
||||
|
||||
@@ -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 |
|
||||
| **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 |
|
||||
|
||||
## Desenvolvimento
|
||||
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
**English** · [Português](SECURITY.pt-BR.md)
|
||||
|
||||
Sabiá processes untrusted data from the air and from `.pcap` files (802.11 frames and handshakes, possibly forged) and controls network interfaces with root privilege. Vulnerabilities — memory corruption in the radiotap/802.11/EAPOL parsers, path escape when writing `.pcap` or exporting, privilege misuse — should be reported privately through the repository's **Security → Report a vulnerability** tab on GitHub. Do not open a public issue before a fix.
|
||||
Sabiá processes untrusted data from the air and from `.pcap` files (802.11 frames and handshakes, possibly forged) and controls network interfaces with root privilege. Vulnerabilities — memory corruption in the radiotap/802.11/EAPOL parsers, path escape when writing `.pcap` or exporting, privilege misuse — should be reported privately through the private vulnerability reporting feature of the repository on Gitea. Do not open a public issue before a fix.
|
||||
|
||||
Sabiá is a tool for **authorized auditing**. Reports about using it to attack third-party networks are out of the project's security scope — see the **Authorized use** section of the [README](README.md).
|
||||
@@ -2,6 +2,6 @@
|
||||
|
||||
[English](SECURITY.md) · **Português**
|
||||
|
||||
O Sabiá processa dados não confiáveis vindos do ar e de arquivos `.pcap` (quadros 802.11 e handshakes, possivelmente forjados) e controla interfaces de rede com privilégio de root. Vulnerabilidades — corrupção de memória nos parsers de radiotap/802.11/EAPOL, escape de caminho na escrita de `.pcap` ou na exportação, uso indevido de privilégio — devem ser reportadas em privado pela aba **Security → Report a vulnerability** do repositório no GitHub. Não abra issue pública antes da correção.
|
||||
O Sabiá processa dados não confiáveis vindos do ar e de arquivos `.pcap` (quadros 802.11 e handshakes, possivelmente forjados) e controla interfaces de rede com privilégio de root. Vulnerabilidades — corrupção de memória nos parsers de radiotap/802.11/EAPOL, escape de caminho na escrita de `.pcap` ou na exportação, uso indevido de privilégio — devem ser reportadas em privado pelo recurso de relato privado de vulnerabilidade do repositório no Gitea. Não abra issue pública antes da correção.
|
||||
|
||||
O Sabiá é uma ferramenta de **auditoria autorizada**. Relatos sobre uso para atacar redes de terceiros não são escopo de segurança do projeto — veja a seção **Uso autorizado** do [README](README.pt-BR.md).
|
||||
@@ -2538,7 +2538,7 @@ checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f"
|
||||
|
||||
[[package]]
|
||||
name = "sabia-core"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
dependencies = [
|
||||
"aes",
|
||||
"flate2",
|
||||
@@ -2555,7 +2555,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sabia-gui"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"sabia-core",
|
||||
|
||||
@@ -3,10 +3,11 @@
|
||||
[package]
|
||||
name = "sabia-gui"
|
||||
description = "Sabiá: interface gráfica de auditoria de Wi-Fi"
|
||||
version = "1.0.0"
|
||||
version = "1.0.3"
|
||||
edition = "2021"
|
||||
license = "MIT OR Apache-2.0"
|
||||
repository = "https://github.com/lucasrguerra/sabia"
|
||||
authors = ["Sabiá contributors"]
|
||||
repository = "https://git.lucasrguerra.dev.br/lucasrguerra/sabia"
|
||||
rust-version = "1.80"
|
||||
|
||||
[workspace]
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
fn main() {
|
||||
println!("cargo:rerun-if-changed=../../../logo.png");
|
||||
let _ = std::fs::copy("../../../logo.png", "../ui/logo.png");
|
||||
#[cfg(windows)]
|
||||
{
|
||||
// Modo monitor e captura crua exigem administrador no Windows: o
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"$schema": "../gen/schemas/desktop-schema.json",
|
||||
"identifier": "default",
|
||||
"description": "Permissões da janela principal",
|
||||
"windows": ["main"],
|
||||
"permissions": ["core:default", "core:window:allow-destroy", "core:window:allow-close"]
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{}
|
||||
{"default":{"identifier":"default","description":"Permissões da janela principal","local":true,"windows":["main"],"permissions":["core:default","core:window:allow-destroy","core:window:allow-close"]}}
|
||||
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 40 KiB After Width: | Height: | Size: 43 KiB |
|
Before Width: | Height: | Size: 2.0 KiB After Width: | Height: | Size: 2.1 KiB |
|
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 17 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 19 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 46 KiB After Width: | Height: | Size: 50 KiB |
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.9 KiB |
|
Before Width: | Height: | Size: 52 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 3.4 KiB |
|
Before Width: | Height: | Size: 6.2 KiB After Width: | Height: | Size: 6.7 KiB |
|
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 9.4 KiB |
|
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 4.0 KiB |
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
|
||||
<background android:drawable="@color/ic_launcher_background"/>
|
||||
</adaptive-icon>
|
||||
|
Before Width: | Height: | Size: 5.8 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 6.8 KiB |
|
Before Width: | Height: | Size: 3.2 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 9.1 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 17 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 86 KiB |
|
Before Width: | Height: | Size: 29 KiB |
@@ -1,4 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="ic_launcher_background">#FFFFFF</color>
|
||||
</resources>
|
||||
|
Before Width: | Height: | Size: 53 KiB After Width: | Height: | Size: 57 KiB |
|
Before Width: | Height: | Size: 112 KiB After Width: | Height: | Size: 122 KiB |
|
Before Width: | Height: | Size: 979 B |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 4.4 KiB |
|
Before Width: | Height: | Size: 8.1 KiB |
|
Before Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 7.1 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 311 KiB |
|
Before Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 6.6 KiB |
|
Before Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 336 KiB After Width: | Height: | Size: 350 KiB |
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"$schema": "https://schema.tauri.app/config/2",
|
||||
"productName": "Sabia",
|
||||
"version": "1.0.0",
|
||||
"productName": "Sabiá",
|
||||
"version": "1.0.3",
|
||||
"identifier": "dev.sabia.app",
|
||||
"build": {
|
||||
"frontendDist": "../ui"
|
||||
|
||||
@@ -4,14 +4,14 @@
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Sabiá</title>
|
||||
<link rel="icon" href="/sabia.png">
|
||||
<link rel="icon" href="/logo.png">
|
||||
<link rel="stylesheet" href="/app.css">
|
||||
</head>
|
||||
<body>
|
||||
<div class="app">
|
||||
<aside class="sidebar">
|
||||
<div class="brand">
|
||||
<img class="logo" src="/sabia.png" alt="">
|
||||
<img class="logo" src="/logo.png" alt="">
|
||||
<div>
|
||||
<strong>Sabiá</strong>
|
||||
<small>auditoria de Wi-Fi</small>
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017-2024 Paged.js contributors
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,10 @@
|
||||
# Paged.js
|
||||
|
||||
`paged.polyfill.min.js` é o polyfill do [Paged.js](https://pagedjs.org/), versão
|
||||
0.4.3, usado para paginar os relatórios em A4 no navegador.
|
||||
|
||||
- Projeto: https://gitlab.coko.foundation/pagedjs/pagedjs
|
||||
- Licença: MIT (texto em `PAGEDJS-LICENSE`)
|
||||
- Arquivo: `paged.polyfill.min.js` (build de distribuição, não modificado)
|
||||
|
||||
O arquivo é vendorizado para que o relatório funcione offline, sem CDN.
|
||||
@@ -17,8 +17,8 @@ const APP_CSS: &str = include_str!("../../../../apps/gui/ui/app.css");
|
||||
const APP_JS: &str = include_str!("../../../../apps/gui/ui/app.js");
|
||||
const I18N_JS: &str = include_str!("../../../../apps/gui/ui/i18n.js");
|
||||
const I18N_EN_JS: &str = include_str!("../../../../apps/gui/ui/i18n-en.js");
|
||||
const LOGO_PNG: &[u8] = include_bytes!("../../../../apps/gui/ui/sabia.png");
|
||||
const PAGED_JS: &str = include_str!("../../../../apps/gui/ui/paged.polyfill.min.js");
|
||||
const LOGO_PNG: &[u8] = include_bytes!("../../../../logo.png");
|
||||
const PAGED_JS: &str = include_str!("../../../../apps/gui/ui/vendor/paged.polyfill.min.js");
|
||||
|
||||
pub fn serve(port: u16, audit_path: std::path::PathBuf, open: bool) -> sabia_core::Result<()> {
|
||||
let token = gen_token();
|
||||
@@ -275,7 +275,7 @@ fn handle(
|
||||
}
|
||||
|
||||
match req.path.as_str() {
|
||||
"/paged.polyfill.min.js" => {
|
||||
"/vendor/paged.polyfill.min.js" | "/paged.polyfill.min.js" => {
|
||||
return respond(
|
||||
&mut stream,
|
||||
200,
|
||||
@@ -308,7 +308,7 @@ fn handle(
|
||||
I18N_EN_JS.as_bytes(),
|
||||
)
|
||||
}
|
||||
"/sabia.png" => return respond(&mut stream, 200, "image/png", LOGO_PNG),
|
||||
"/logo.png" => return respond(&mut stream, 200, "image/png", LOGO_PNG),
|
||||
_ => {}
|
||||
}
|
||||
|
||||
|
||||
@@ -1198,6 +1198,7 @@ impl Engine {
|
||||
|
||||
/// `direction`: `ap` (AP→STA), `sta` (STA→AP, exige estação) ou `both`.
|
||||
/// `frame_kind`: `deauth` ou `disassoc`.
|
||||
#[cfg(target_os = "linux")]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn deauth_directional(
|
||||
&self,
|
||||
|
||||
@@ -34,12 +34,12 @@ pub struct ReportOptions {
|
||||
}
|
||||
|
||||
const PAGED_MARKER: &str = "<!--SABIA_PAGEDJS-->";
|
||||
const LOGO_PNG: &[u8] = include_bytes!("../../../apps/gui/ui/sabia.png");
|
||||
const LOGO_PNG: &[u8] = include_bytes!("../../../logo.png");
|
||||
const B64: &[u8; 64] = b"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
|
||||
/// Embute o logo como `data:` URI — sem isso o relatório standalone (modo
|
||||
/// offline, pensado pra abrir sem servidor nenhum) teria um `<img
|
||||
/// src="/sabia.png">` que nunca resolve fora do `sabia ui`. Pequeno o
|
||||
/// src="/logo.png">` que nunca resolve fora do `sabia ui`. Pequeno o
|
||||
/// bastante (poucos KB) pra não valer a pena puxar uma crate de base64 só
|
||||
/// por isso.
|
||||
fn base64(bytes: &[u8]) -> String {
|
||||
@@ -502,7 +502,7 @@ pub fn render(model: &ReportModel, options: ReportOptions) -> String {
|
||||
let paged = if options.inline_pagedjs {
|
||||
PAGED_MARKER.to_string()
|
||||
} else {
|
||||
"<script src=\"/paged.polyfill.min.js\"></script>".to_string()
|
||||
"<script src=\"/vendor/paged.polyfill.min.js\"></script>".to_string()
|
||||
};
|
||||
let stats = &model.stats;
|
||||
let crackable = model.handshakes.iter().filter(|h| h.crackable).count();
|
||||
@@ -765,7 +765,7 @@ mod tests {
|
||||
m.title = "Rede <x>".into();
|
||||
let html = render(&m, ReportOptions::default());
|
||||
assert!(html.contains("Rede <x>"));
|
||||
assert!(html.contains("paged.polyfill.min.js"));
|
||||
assert!(html.contains("/vendor/paged.polyfill.min.js"));
|
||||
assert!(html.contains("@page"));
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
//! 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::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)]
|
||||
struct Release {
|
||||
@@ -92,8 +98,8 @@ fn asset_suffix_for_platform() -> String {
|
||||
/// Consulta a última release publicada e compara com a versão atual
|
||||
/// (`CARGO_PKG_VERSION` do binário chamador).
|
||||
pub fn check(current_version: &str) -> Result<UpdateStatus> {
|
||||
let url = format!("https://api.github.com/repos/{REPO}/releases/latest");
|
||||
let body = match curl_text(&url) {
|
||||
let url = LATEST_RELEASE_URL;
|
||||
let body = match curl_text(url) {
|
||||
Ok(b) => b,
|
||||
Err(_) => {
|
||||
// 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.
|
||||
/// Retorna a versão instalada.
|
||||
pub fn apply(current_exe: &PathBuf) -> Result<String> {
|
||||
let url = format!("https://api.github.com/repos/{REPO}/releases/latest");
|
||||
let body = curl_text(&url)?;
|
||||
// O pacote da release contém o CLI (`sabia`). Trocar por ele um executável que
|
||||
// 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 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(),
|
||||
})
|
||||
}
|
||||
|
||||
#[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}");
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
//! Ataque de PIN WPS com o `reaver` embutido.
|
||||
//!
|
||||
//! O protocolo EAP-WSC é grande demais para reimplementar aqui; o Sabiá
|
||||
//! carrega uma cópia do reaver (GPL-2, veja `assets/tools/LICENSE.reaver`),
|
||||
//! carrega uma cópia do reaver (GPL-2, veja `crates/sabia-core/assets/tools/LICENSE.reaver`),
|
||||
//! extrai em tempo de execução e mostra a saída ao vivo. Assim o WPS já
|
||||
//! funciona sem instalar nada. Se o usuário tiver um `bully` no PATH, ele
|
||||
//! também aparece como alternativa.
|
||||
@@ -17,7 +17,7 @@ pub struct WpsTool {
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
const BUNDLED_REAVER: &[u8] = include_bytes!("../../../assets/tools/reaver");
|
||||
const BUNDLED_REAVER: &[u8] = include_bytes!("../assets/tools/reaver");
|
||||
|
||||
/// Procura um binário no `PATH` — usado pelo WPS (reaver/bully) e, fora
|
||||
/// deste módulo, pelo Evil Twin (hostapd/dnsmasq), que precisa da mesma
|
||||
|
||||
@@ -28,13 +28,13 @@ The CLI and the desktop app are thin clients: both call the core's `Engine`. The
|
||||
| `pcapreader.rs`, `pcapwriter.rs` | `.pcap` read and write. |
|
||||
| `audit.rs` | Local hash-chained audit trail. |
|
||||
| `doctor.rs` | Diagnostics of privileges, tools and interfaces. |
|
||||
| `update.rs` | Self-update from GitHub releases (curl + SHA-256). |
|
||||
| `update.rs` | Self-update from Gitea releases (curl + SHA-256). |
|
||||
| `error.rs`, `testutil.rs` | Shared error type; test helpers. |
|
||||
|
||||
## Platform
|
||||
|
||||
Capture and monitor mode are **Linux** (`AF_PACKET` + netlink). Parsing (`radiotap`, `dot11`, `eapol`, `handshake`), cracking (`crack`) and `.pcap` are cross-platform, being offline — which is why the tests run on Linux, Windows and macOS in CI.
|
||||
Capture and monitor mode are **Linux** (`AF_PACKET` + netlink). Parsing (`radiotap`, `dot11`, `eapol`, `handshake`), cracking (`crack`) and `.pcap` are cross-platform, being offline — which is why the tests run on Linux in CI.
|
||||
|
||||
## UI transport
|
||||
|
||||
`sabia ui` starts a server on `127.0.0.1` protected by a token generated on the spot; it serves `index.html`/`app.css`/`app.js`/`sabia.png` embedded in the binary plus a JSON API. The Tauri app uses the same files, calling the `Engine` directly via Tauri commands instead of HTTP.
|
||||
`sabia ui` starts a server on `127.0.0.1` protected by a token generated on the spot; it serves `index.html`/`app.css`/`app.js`/`logo.png` embedded in the binary plus a JSON API. The Tauri app uses the same files, calling the `Engine` directly via Tauri commands instead of HTTP.
|
||||
@@ -28,13 +28,13 @@ A CLI e o app desktop são clientes finos: ambos chamam o `Engine` do núcleo. A
|
||||
| `pcapreader.rs`, `pcapwriter.rs` | Leitura e escrita de `.pcap`. |
|
||||
| `audit.rs` | Trilha de auditoria local encadeada por hash. |
|
||||
| `doctor.rs` | Diagnóstico de privilégios, ferramentas e interfaces. |
|
||||
| `update.rs` | Auto-atualização por releases do GitHub (curl + SHA-256). |
|
||||
| `update.rs` | Auto-atualização por releases do Gitea (curl + SHA-256). |
|
||||
| `error.rs`, `testutil.rs` | Tipo de erro compartilhado; ajudantes de teste. |
|
||||
|
||||
## Plataforma
|
||||
|
||||
Captura e modo monitor são **Linux** (`AF_PACKET` + netlink). Parsing (`radiotap`, `dot11`, `eapol`, `handshake`), quebra (`crack`) e `.pcap` são multiplataforma, por serem offline — por isso os testes rodam em Linux, Windows e macOS no CI.
|
||||
Captura e modo monitor são **Linux** (`AF_PACKET` + netlink). Parsing (`radiotap`, `dot11`, `eapol`, `handshake`), quebra (`crack`) e `.pcap` são multiplataforma, por serem offline — por isso os testes rodam em Linux no CI.
|
||||
|
||||
## Transporte da UI
|
||||
|
||||
`sabia ui` sobe um servidor em `127.0.0.1` protegido por um token gerado na hora; serve `index.html`/`app.css`/`app.js`/`sabia.png` embutidos no binário e uma API JSON. O app Tauri usa os mesmos arquivos, chamando o `Engine` direto por comandos Tauri em vez de HTTP.
|
||||
`sabia ui` sobe um servidor em `127.0.0.1` protegido por um token gerado na hora; serve `index.html`/`app.css`/`app.js`/`logo.png` embutidos no binário e uma API JSON. O app Tauri usa os mesmos arquivos, chamando o `Engine` direto por comandos Tauri em vez de HTTP.
|
||||
@@ -0,0 +1,67 @@
|
||||
# O que o Sabiá suporta
|
||||
|
||||
[English](../support.md) · **Português**
|
||||
|
||||
Matriz do que existe hoje (versão 1.0.3). Para o que vem a seguir, veja o [ROADMAP](../../ROADMAP.pt-BR.md).
|
||||
|
||||
## Plataformas
|
||||
|
||||
| | Descoberta e captura | Análise e quebra offline | App desktop |
|
||||
|---|---|---|---|
|
||||
| Linux | ✅ root, adaptador em modo monitor | ✅ | ✅ AppImage e deb |
|
||||
| Windows | ❌ | ✅ `.pcap`, dicionário/regras/máscara | ✅ instalador |
|
||||
| macOS | ❌ | ✅ `.pcap`, dicionário/regras/máscara | ❌ sem build |
|
||||
|
||||
A captura ao vivo usa `AF_PACKET` e netlink; exige Linux, root (ou `CAP_NET_ADMIN`/`CAP_NET_RAW`) e uma placa com modo monitor. Injeção (`deauth`, Evil Twin) exige uma placa que também injete. O app desktop pede elevação ao abrir.
|
||||
|
||||
## Descoberta
|
||||
|
||||
- Varredura dos canais de **2.4, 5 e 6 GHz**, limitada pela placa e pelo _regulatory domain_.
|
||||
- **Pontos de acesso e clientes** com SSID, BSSID, canal, sinal, tipo de segurança, detalhes de WPS e fabricante.
|
||||
- **Modo monitor** ligado/desligado por netlink/`iw`, com recuperação automática se o NetworkManager retomar a interface.
|
||||
- **Canais suportados** informados por rádio.
|
||||
|
||||
## Captura e análise
|
||||
|
||||
| Item | Suporte |
|
||||
|---|---|
|
||||
| 4-way handshake (EAPOL) | ✅ detectado e guardado para quebra offline |
|
||||
| PMKID | ✅ detectado e guardado, inclusive caça sem cliente |
|
||||
| Escrita e leitura de `.pcap` | ✅ compatível com as outras ferramentas 802.11 |
|
||||
| Decifração CCMP | ✅ para um cliente cuja PSK já é conhecida |
|
||||
| Parsers radiotap / 802.11 / EAPOL | ✅ |
|
||||
|
||||
## Quebra (WPA/WPA2-PSK)
|
||||
|
||||
| Modo | Suporte |
|
||||
|---|---|
|
||||
| Dicionário | ✅ rockyou + Probable-Wordlists por padrão (9,7 milhões de senhas) |
|
||||
| Regras de mutação | ✅ estilo hashcat |
|
||||
| Máscara/força bruta | ✅ `?d?l?u?s?a` |
|
||||
| Verificação | ✅ PMK por PBKDF2-HMAC-SHA1 + MIC (4-way) ou PMKID |
|
||||
| Exportação | ✅ `.hc22000` para hashcat/John |
|
||||
|
||||
WPA3/SAE **não** cai por dicionário de handshake; o escopo da quebra é WPA/WPA2-PSK.
|
||||
|
||||
## Ataques autorizados
|
||||
|
||||
- **Desautenticação** direcionada ou contínua, com `--yes` / confirmação explícita de autorização.
|
||||
- **Teste de PMF** e **caça de PMKID sem cliente**.
|
||||
- **WPS PIN / Pixie Dust** com o `reaver` embutido (GPL-2) ou `bully` quando instalado.
|
||||
- **Evil Twin PSK**: clona um AP visto com portal cativo; a senha só é aceita depois de conferida contra o handshake real já capturado.
|
||||
- **Evil Twin Enterprise** (802.1X/EAP): captura credenciais automaticamente, sem portal. Exige `hostapd-wpe` e uma segunda interface Wi-Fi em modo AP.
|
||||
|
||||
## Relatórios, auditoria e diagnóstico
|
||||
|
||||
- **Relatório de auditoria paginado** (capa, sumário, achados, pontuação de canais, trilha de auditoria), gerado localmente.
|
||||
- **Trilha de auditoria encadeada por hash** para toda ativação de modo monitor e sessão de captura (`sabia audit show`).
|
||||
- **`sabia doctor`** confere privilégios, ferramentas e interfaces antes da captura.
|
||||
|
||||
## O que o Sabiá não faz
|
||||
|
||||
- Quebrar handshake WPA3/SAE, ou recuperar senha sem handshake/PMKID capturado e uma candidata correspondente.
|
||||
- Capturar no Windows ou no macOS; nessas plataformas só há análise offline.
|
||||
- Dispensar a confirmação de autorização para `deauth`, WPS ou Evil Twin.
|
||||
- Garantir cobertura de 5/6 GHz: depende da placa, do _regulatory domain_ e do driver.
|
||||
- Fazer um cliente enterprise confiar num certificado autoassinado quando ele valida a cadeia (comum em perfil corporativo gerenciado).
|
||||
- Acelerar a quebra por GPU ainda; a quebra é feita na CPU.
|
||||
@@ -0,0 +1,67 @@
|
||||
# What Sabiá supports
|
||||
|
||||
[English](support.md) · [Português](pt-BR/suporte.md)
|
||||
|
||||
Matrix of what exists today (version 1.0.3). For what comes next, see the [ROADMAP](../ROADMAP.md).
|
||||
|
||||
## Platforms
|
||||
|
||||
| | Discovery and capture | Offline analysis and cracking | Desktop app |
|
||||
|---|---|---|---|
|
||||
| Linux | ✅ root, adapter in monitor mode | ✅ | ✅ AppImage and deb |
|
||||
| Windows | ❌ | ✅ `.pcap`, wordlist/rules/mask | ✅ installer |
|
||||
| macOS | ❌ | ✅ `.pcap`, wordlist/rules/mask | ❌ no build |
|
||||
|
||||
Live capture uses `AF_PACKET` and netlink; it needs Linux, root (or `CAP_NET_ADMIN`/`CAP_NET_RAW`) and an adapter that supports monitor mode. Injection (`deauth`, Evil Twin) needs an adapter that also supports injection. The desktop app asks for elevation on startup.
|
||||
|
||||
## Discovery
|
||||
|
||||
- **2.4, 5 and 6 GHz** channel sweep, limited by the adapter and the regulatory domain.
|
||||
- **Access points and clients** with SSID, BSSID, channel, signal, security type, WPS details and vendor.
|
||||
- **Monitor mode** on/off through netlink/`iw`, with automatic recovery if NetworkManager takes the interface back.
|
||||
- **Supported channels** reported per radio.
|
||||
|
||||
## Capture and analysis
|
||||
|
||||
| Item | Support |
|
||||
|---|---|
|
||||
| 4-way handshake (EAPOL) | ✅ detected and stored for offline cracking |
|
||||
| PMKID | ✅ detected and stored, including clientless PMKID hunting |
|
||||
| `.pcap` write and read | ✅ compatible with other 802.11 tools |
|
||||
| CCMP decryption | ✅ for a client whose PSK is already known |
|
||||
| Radiotap / 802.11 / EAPOL parsing | ✅ |
|
||||
|
||||
## Cracking (WPA/WPA2-PSK)
|
||||
|
||||
| Mode | Support |
|
||||
|---|---|
|
||||
| Dictionary | ✅ rockyou + Probable-Wordlists by default (9.7 million passwords) |
|
||||
| Mutation rules | ✅ hashcat-style rules |
|
||||
| Mask/brute force | ✅ `?d?l?u?s?a` |
|
||||
| Verification | ✅ PBKDF2-HMAC-SHA1 PMK + MIC (4-way) or PMKID |
|
||||
| Export | ✅ `.hc22000` for hashcat/John |
|
||||
|
||||
WPA3/SAE does **not** fall to a handshake dictionary attack; the cracking scope is WPA/WPA2-PSK.
|
||||
|
||||
## Authorized attacks
|
||||
|
||||
- **Deauthentication** directed or continuous, with `--yes` / explicit authorization checkbox.
|
||||
- **PMF test** and **clientless PMKID hunt**.
|
||||
- **WPS PIN / Pixie Dust** using the bundled `reaver` (GPL-2) or `bully` when installed.
|
||||
- **Evil Twin PSK**: clones a seen AP with a captive portal; the password is only accepted after being checked against the real captured handshake.
|
||||
- **Evil Twin Enterprise** (802.1X/EAP): captures credentials automatically, without a portal. It needs `hostapd-wpe` and a second Wi-Fi interface in AP mode.
|
||||
|
||||
## Reports, audit and diagnostics
|
||||
|
||||
- **Paginated audit report** (cover, table of contents, findings, channel score, audit trail), generated locally.
|
||||
- **Hash-chained audit trail** for every monitor-mode activation and capture session (`sabia audit show`).
|
||||
- **`sabia doctor`** checks privileges, tools and interfaces before capture.
|
||||
|
||||
## What Sabiá does not do
|
||||
|
||||
- Crack WPA3/SAE handshakes, or recover a password without a captured handshake/PMKID and a matching candidate.
|
||||
- Capture on Windows or macOS; those platforms are offline analysis only.
|
||||
- Bypass the authorization requirement for deauth, WPS or Evil Twin.
|
||||
- Guarantee 5/6 GHz coverage: it depends on the adapter, regulatory domain and driver.
|
||||
- Make an enterprise client trust a self-signed certificate when the client validates the chain (common in managed corporate profiles).
|
||||
- Provide GPU acceleration yet; cracking is CPU-bound.
|
||||
@@ -16,15 +16,15 @@ mkdir -p "$cache/partial"
|
||||
trap 'sudo chown -R "$(id -u):$(id -g)" "$cache" 2>/dev/null || true; rm -f "$cache/lock"' EXIT
|
||||
opts=(-o "Dir::Cache::archives=$cache" -y)
|
||||
|
||||
# 1) com .deb em cache: instala direto, sem nem atualizar o índice do apt
|
||||
if ls "$cache"/*.deb >/dev/null 2>&1 && timeout 300 sudo apt-get "${opts[@]}" install "${pkgs[@]}"; then
|
||||
# 1) com .deb em cache e índice presente: instala direto, sem nem atualizar o índice do apt
|
||||
if [ -z "$(dpkg --print-foreign-architectures)" ] && ls "$cache"/*.deb >/dev/null 2>&1 && ls /var/lib/apt/lists/*Packages* >/dev/null 2>&1 && timeout 300 sudo apt-get "${opts[@]}" install "${pkgs[@]}"; then
|
||||
echo "instalado a partir do cache"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 2) sem cache (ou incompleto): atualiza e instala, até 3 tentativas com tempo limite
|
||||
# 2) sem cache (ou multiarch / incompleto): atualiza e instala, até 3 tentativas com tempo limite
|
||||
for tentativa in 1 2 3; do
|
||||
if timeout 240 sudo apt-get update && timeout 600 sudo apt-get "${opts[@]}" install "${pkgs[@]}"; then
|
||||
if timeout 240 sudo apt-get update && { [ -z "$(dpkg --print-foreign-architectures)" ] || timeout 600 sudo apt-get "${opts[@]}" upgrade; } && timeout 600 sudo apt-get "${opts[@]}" install "${pkgs[@]}"; then
|
||||
echo "instalado (tentativa $tentativa)"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
#!/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|linux-x86_64|linux-aarch64|windows|windows-docker>}
|
||||
tauri() { npx --yes @tauri-apps/cli@2 "$@"; }
|
||||
|
||||
if [ -z "${TAURI_SIGNING_PRIVATE_KEY:-}" ] && [ -f "$HOME/.tauri/sabia-updater.key" ]; then
|
||||
export TAURI_SIGNING_PRIVATE_KEY="$(cat "$HOME/.tauri/sabia-updater.key")"
|
||||
[ -f "$HOME/.tauri/sabia-updater.pass" ] && export TAURI_SIGNING_PRIVATE_KEY_PASSWORD="$(cat "$HOME/.tauri/sabia-updater.pass")"
|
||||
fi
|
||||
|
||||
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)" \
|
||||
-e TAURI_SIGNING_PRIVATE_KEY -e TAURI_SIGNING_PRIVATE_KEY_PASSWORD \
|
||||
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
|
||||
|
||||
rm -rf dist-gui
|
||||
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
|
||||
|
||||
tauri_blob_cache="$HOME/.cache/tauri"
|
||||
if [ -d /opt/hostedtoolcache ] && [ -w /opt/hostedtoolcache ]; then
|
||||
tauri_blob_cache="/opt/hostedtoolcache/tauri-tools"
|
||||
fi
|
||||
mkdir -p "$tauri_blob_cache" "$HOME/.cache/tauri"
|
||||
|
||||
fetch_cached_tool() {
|
||||
local name=$1 url=$2 patch_elf=${3:-0}
|
||||
local cached="$tauri_blob_cache/$name"
|
||||
if [ ! -s "$cached" ]; then
|
||||
local tmp="$cached.tmp.$$"
|
||||
curl -fsSL "$url" -o "$tmp"
|
||||
if [ "$patch_elf" = 1 ]; then
|
||||
dd if=/dev/zero of="$tmp" bs=1 count=3 seek=8 conv=notrunc 2>/dev/null
|
||||
fi
|
||||
chmod +x "$tmp"
|
||||
mv -f "$tmp" "$cached"
|
||||
fi
|
||||
if [ "$cached" != "$HOME/.cache/tauri/$name" ]; then
|
||||
cp -f "$cached" "$HOME/.cache/tauri/$name"
|
||||
chmod +x "$HOME/.cache/tauri/$name"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$platform" in
|
||||
linux|linux-aarch64)
|
||||
bundle="${CARGO_TARGET_DIR:-target}/release/bundle"
|
||||
rm -rf "$bundle/appimage" "$bundle/deb"
|
||||
tauri build --bundles appimage,deb
|
||||
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
|
||||
;;
|
||||
linux-x86_64)
|
||||
if [ "$(uname -m)" = x86_64 ]; then
|
||||
bundle="${CARGO_TARGET_DIR:-target}/release/bundle"
|
||||
rm -rf "$bundle/appimage" "$bundle/deb"
|
||||
tauri build --bundles appimage,deb
|
||||
else
|
||||
rustup target add x86_64-unknown-linux-gnu >/dev/null
|
||||
mkdir -p /tmp/x86_64-cross-tools "$HOME/.cache/tauri"
|
||||
cat >/tmp/x86_64-cross-tools/ldd <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
exec qemu-x86_64 /lib64/ld-linux-x86-64.so.2 --list "$@"
|
||||
EOF
|
||||
chmod +x /tmp/x86_64-cross-tools/ldd
|
||||
ln -sf /usr/bin/x86_64-linux-gnu-strip /tmp/x86_64-cross-tools/strip
|
||||
for tool_rel in glib-2.0/gio-querymodules gdk-pixbuf-2.0/gdk-pixbuf-query-loaders libgtk-3-0t64/gtk-query-immodules-3.0 libgtk-3-0/gtk-query-immodules-3.0; do
|
||||
if [ -x "/usr/lib/x86_64-linux-gnu/$tool_rel" ]; then
|
||||
tname=$(basename "$tool_rel")
|
||||
printf '#!/usr/bin/env bash\nexec qemu-x86_64 "/usr/lib/x86_64-linux-gnu/%s" "$@"\n' "$tool_rel" >"/tmp/x86_64-cross-tools/$tname"
|
||||
chmod +x "/tmp/x86_64-cross-tools/$tname"
|
||||
fi
|
||||
done
|
||||
fetch_cached_tool "linuxdeploy-07333c6-aarch64.AppImage" \
|
||||
"https://github.com/tauri-apps/binary-releases/releases/download/linuxdeploy-07333c6/linuxdeploy-aarch64.AppImage" 1
|
||||
fetch_cached_tool "linuxdeploy-plugin-appimage.AppImage" \
|
||||
"https://github.com/linuxdeploy/linuxdeploy-plugin-appimage/releases/download/continuous/linuxdeploy-plugin-appimage-aarch64.AppImage" 1
|
||||
fetch_cached_tool "runtime-x86_64" \
|
||||
"https://github.com/AppImage/type2-runtime/releases/download/continuous/runtime-x86_64" 0
|
||||
cat >"$HOME/.cache/tauri/linuxdeploy-07333c6-x86_64.AppImage" <<'EOF'
|
||||
#!/usr/bin/env bash
|
||||
export ARCH=x86_64
|
||||
export NO_STRIP=true
|
||||
export APPIMAGE_EXTRACT_AND_RUN=1
|
||||
export LDAI_RUNTIME_FILE="$HOME/.cache/tauri/runtime-x86_64"
|
||||
export LD_GTK_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu
|
||||
export DEB_HOST_MULTIARCH=x86_64-linux-gnu
|
||||
export PKG_CONFIG_ALLOW_CROSS=1
|
||||
export PKG_CONFIG_LIBDIR=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig
|
||||
export PATH="/tmp/x86_64-cross-tools:$PATH"
|
||||
exec "$HOME/.cache/tauri/linuxdeploy-07333c6-aarch64.AppImage" "$@"
|
||||
EOF
|
||||
chmod +x "$HOME/.cache/tauri/linuxdeploy-07333c6-x86_64.AppImage"
|
||||
export PKG_CONFIG_ALLOW_CROSS=1
|
||||
export PKG_CONFIG_LIBDIR=/usr/lib/x86_64-linux-gnu/pkgconfig:/usr/share/pkgconfig
|
||||
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
|
||||
export CXX_x86_64_unknown_linux_gnu=x86_64-linux-gnu-g++
|
||||
bundle="${CARGO_TARGET_DIR:-target}/x86_64-unknown-linux-gnu/release/bundle"
|
||||
rm -rf "$bundle/appimage" "$bundle/deb"
|
||||
tauri build --target x86_64-unknown-linux-gnu --bundles appimage,deb
|
||||
fi
|
||||
cp "$bundle"/appimage/*.AppImage "$bundle"/deb/*.deb ../../../dist-gui/
|
||||
cp "$bundle"/appimage/*.AppImage.sig ../../../dist-gui/ 2>/dev/null || true
|
||||
;;
|
||||
windows)
|
||||
rustup target add x86_64-pc-windows-msvc >/dev/null
|
||||
bundle="${CARGO_TARGET_DIR:-target}/x86_64-pc-windows-msvc/release/bundle/nsis"
|
||||
rm -rf "$bundle"
|
||||
tauri build --runner cargo-xwin --target x86_64-pc-windows-msvc --bundles 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
|
||||
@@ -250,7 +250,7 @@ function rustLabels(source, file) {
|
||||
// ---------------------------------------------------------------- coleta
|
||||
|
||||
const uiFiles = readdirSync(join(root, 'apps/gui/ui'))
|
||||
.filter((f) => f.endsWith('.js') && f !== 'i18n-en.js' && f !== 'paged.polyfill.min.js')
|
||||
.filter((f) => f.endsWith('.js') && f !== 'i18n-en.js' && f !== 'vendor')
|
||||
.map((f) => join(root, 'apps/gui/ui', f));
|
||||
uiFiles.push(join(root, 'apps/gui/ui/index.html'));
|
||||
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Gera, a partir da logo oficial, o ícone base usado pelo Tauri.
|
||||
|
||||
A logo oficial é `logo.png` na raiz do repositório (RGBA, fundo transparente,
|
||||
serve aos temas claro e escuro). Ela não é alterada aqui. Saída:
|
||||
apps/gui/src-tauri/icons/source.png 1024 px, a arte sobre o fundo escuro com cantos arredondados;
|
||||
o CI e `npx @tauri-apps/cli icon` geram o resto dos ícones.
|
||||
|
||||
Uso: python3 scripts/gerar-logo.py
|
||||
"""
|
||||
import os
|
||||
from PIL import Image, ImageDraw
|
||||
|
||||
RAIZ = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
|
||||
ICONES = os.path.join(RAIZ, "apps/gui/src-tauri/icons")
|
||||
FUNDO_ICONE = (10, 16, 28) # o azul-noite do fundo da arte original
|
||||
|
||||
|
||||
def recortar_quadrado(im, margem=0.04):
|
||||
"""Recorta no que é visível (opacidade > 16, ignora resquícios) e centraliza num quadrado."""
|
||||
box = im.getchannel("A").point(lambda v: 255 if v > 16 else 0).getbbox()
|
||||
im = im.crop(box)
|
||||
w, h = im.size
|
||||
lado = round(max(w, h) * (1 + 2 * margem))
|
||||
quadro = Image.new("RGBA", (lado, lado), (0, 0, 0, 0))
|
||||
quadro.paste(im, ((lado - w) // 2, (lado - h) // 2), im)
|
||||
return quadro
|
||||
|
||||
|
||||
def main():
|
||||
logo = recortar_quadrado(Image.open(os.path.join(RAIZ, "logo.png")).convert("RGBA"))
|
||||
|
||||
lado = 1024
|
||||
fundo = Image.new("RGBA", (lado, lado), FUNDO_ICONE + (255,))
|
||||
arte = logo.resize((round(lado * 0.82),) * 2, Image.LANCZOS)
|
||||
fundo.paste(arte, ((lado - arte.width) // 2, (lado - arte.height) // 2), arte)
|
||||
mascara = Image.new("L", (lado, lado), 0)
|
||||
ImageDraw.Draw(mascara).rounded_rectangle((0, 0, lado - 1, lado - 1), radius=round(lado * 0.2237), fill=255)
|
||||
icone = Image.new("RGBA", (lado, lado), (0, 0, 0, 0))
|
||||
icone.paste(fundo, (0, 0), mascara)
|
||||
os.makedirs(ICONES, exist_ok=True)
|
||||
destino = os.path.join(ICONES, "source.png")
|
||||
icone.save(destino, optimize=True)
|
||||
os.chmod(destino, 0o644)
|
||||
print("icons/source.png")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/usr/bin/env bash
|
||||
# Liga o hook de pre-push do repositório (roda scripts/verificar.sh antes de cada push).
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
git config core.hooksPath .githooks
|
||||
echo "hook ligado: git push passa a rodar scripts/verificar.sh (desligue com: git config --unset core.hooksPath)"
|
||||
@@ -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.3 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"
|
||||