Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/scripts/prepare-sandbox.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-3.0-or-later
# Configure an ephemeral Ubuntu CI runner, never a user's installation.
set -euo pipefail

# Use Ubuntu's packaged policy, including the child profile that denies
# capabilities. Do not disable AppArmor or the global userns restriction.
# https://discourse.ubuntu.com/t/understanding-apparmor-user-namespace-restriction/58007
sudo apt-get install -y --no-install-recommends apparmor-profiles
profile=/usr/share/apparmor/extra-profiles/bwrap-userns-restrict
test -f "$profile"
sudo apparmor_parser --replace "$profile"
sudo grep -qx 'bwrap (enforce)' /sys/kernel/security/apparmor/profiles
sudo grep -qx 'unpriv_bwrap (enforce)' /sys/kernel/security/apparmor/profiles

# Run as the ordinary runner user. Fail before compiling if namespaces or
# child confinement are unavailable; the application self-test follows later.
/usr/bin/bwrap --unshare-all --die-with-parent --new-session --clearenv \
--cap-drop ALL --ro-bind /usr /usr \
--symlink usr/bin /bin --symlink usr/lib /lib --symlink usr/lib64 /lib64 \
--proc /proc --dev /dev --remount-ro /dev \
/bin/sh -eu -c '
test ! -e /home
test ! -e /run
test ! -e /etc
# Ubuntu awk resolves through /etc/alternatives, which is deliberately
# absent. Shell builtins keep this probe independent of those links.
seen_cap=false
while read -r key value rest; do
if test "$key" = CapEff:; then
test "$value" = 0000000000000000
seen_cap=true
fi
done < /proc/self/status
test "$seen_cap" = true
seen_loopback=false
while read -r interface rest; do
case "$interface" in
lo:) seen_loopback=true ;;
*:) exit 1 ;;
esac
done < /proc/net/dev
test "$seen_loopback" = true
'
120 changes: 110 additions & 10 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ jobs:
- name: install libdav1d-dev
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends libdav1d-dev pkg-config
sudo apt-get install -y --no-install-recommends libdav1d-dev pkg-config bubblewrap util-linux
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
Expand All @@ -50,52 +50,78 @@ jobs:
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libgtk-4-dev libadwaita-1-dev libdav1d-dev pkg-config
libgtk-4-dev libadwaita-1-dev libdav1d-dev pkg-config bubblewrap util-linux
- uses: dtolnay/rust-toolchain@stable
with:
components: clippy
- uses: Swatinem/rust-cache@v2
- run: cargo clippy -p bigiris --features "gui ai" --all-targets --locked -- -D warnings
- run: cargo clippy --workspace --all-features --all-targets --locked -- -D warnings

test:
name: test (headless)
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: install libdav1d-dev
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends libdav1d-dev pkg-config
sudo apt-get install -y --no-install-recommends libdav1d-dev pkg-config bubblewrap util-linux
- name: prepare distro Bubblewrap policy and verify isolation
run: bash .github/scripts/prepare-sandbox.sh
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# Sandbox tests exercise the distro bubblewrap policy and fail closed.
# Do not disable AppArmor or add broad container capabilities to pass.
- run: cargo test --workspace --all-targets --locked

test-gui:
name: test (gui + ai)
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: install GTK4/libadwaita/dav1d dev
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libgtk-4-dev libadwaita-1-dev libdav1d-dev pkg-config
libgtk-4-dev libadwaita-1-dev libdav1d-dev pkg-config bubblewrap util-linux xvfb dbus-x11
- name: prepare distro Bubblewrap policy and verify isolation
run: bash .github/scripts/prepare-sandbox.sh
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# Tests in `crates/bigiris/src/gui.rs` (the 20 anchor/zoom tests)
# only compile with `--features gui`, so they need their own job.
- run: cargo test -p bigiris --features "gui ai" --all-targets --locked
# Sandbox tests exercise the distro bubblewrap policy and fail closed.
# Do not disable AppArmor or add broad container capabilities to pass.
- run: cargo test --workspace --all-features --all-targets --locked

- name: viewer lifecycle and painted zoom frames (isolated display)
env:
GSETTINGS_BACKEND: memory
GDK_BACKEND: x11
GSK_RENDERER: cairo
BIGIRIS_TEST_ZOOM_REPORT_DIR: ${{ github.workspace }}/target/quality/ci-zoom
run: |
xvfb-run -a dbus-run-session -- cargo test -p bigiris --features "gui ai" \
--locked viewer_lifecycle_releases_state_and_workers -- \
--ignored --test-threads=1
- uses: actions/upload-artifact@v4
if: always()
with:
name: painted-zoom-frames
path: target/quality/ci-zoom

build:
name: build (release, gui + ai)
runs-on: ubuntu-latest
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: install GTK4/libadwaita/dav1d dev
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends \
libgtk-4-dev libadwaita-1-dev libdav1d-dev pkg-config
libgtk-4-dev libadwaita-1-dev libdav1d-dev pkg-config bubblewrap util-linux
- name: prepare distro Bubblewrap policy and verify isolation
run: bash .github/scripts/prepare-sandbox.sh
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
# `ai` usa load-dynamic do ort: compila sem libonnxruntime presente,
Expand Down Expand Up @@ -144,6 +170,8 @@ jobs:
# Usa PKGBUILD.local (build do checkout local, sem clone remoto).
# O PKGBUILD principal usa `source=git+https://github.com/xathay/bigiris`,
# que makepkg nao consegue clonar em repo privado sem credenciais.
# Docker blocks nested namespaces by default. Run functional sandbox
# tests in the host test/build jobs; this job checks build + packaging.
- name: makepkg
working-directory: pkgbuild
run: |
Expand All @@ -155,3 +183,75 @@ jobs:
--skipinteg \
--nocheck \
--clean

nautilus:
name: Nautilus extension contracts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: PYTHONDONTWRITEBYTECODE=1 python3 -m unittest discover -s tests -v
- run: glib-compile-schemas --strict --dry-run data

security:
name: RustSec dependency audit
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo install cargo-audit --locked
- run: cargo audit --deny unsound

stability:
name: Synthetic core stress and stability
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: install codec dev files
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends libdav1d-dev pkg-config bubblewrap util-linux
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo build --release --locked -p bigimage-core --example quality_probe
- run: ./target/release/examples/quality_probe stress 32
- run: ./target/release/examples/quality_probe soak 30

memcheck:
name: Core memory errors and leaks (Valgrind)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: install codec and debugger files
run: |
sudo apt-get update
sudo apt-get install -y --no-install-recommends libdav1d-dev pkg-config bubblewrap util-linux valgrind libc6-dbg
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
- run: cargo build --release --locked -p bigimage-core --example quality_probe
- name: instrument synthetic decode/resize/preview/encode cycles
run: |
valgrind --leak-check=full --show-leak-kinds=definite,indirect \
--errors-for-leak-kinds=definite,indirect --error-exitcode=1 \
./target/release/examples/quality_probe soak 1 320x240

fuzz:
name: Codec and ICC fuzzing (ASan + LSan)
runs-on: ubuntu-latest
# The repository selects stable in rust-toolchain.toml. Installing
# nightly alone does not override that choice for the Python runner.
env:
RUSTUP_TOOLCHAIN: nightly
steps:
- uses: actions/checkout@v4
- run: sudo apt-get update && sudo apt-get install -y libdav1d-dev pkg-config
- uses: dtolnay/rust-toolchain@nightly
- uses: Swatinem/rust-cache@v2
- run: python3 scripts/fuzz.py --seconds 30
- uses: actions/upload-artifact@v4
if: always()
with:
name: fuzz-evidence
path: |
target/quality/fuzz
target/quality/fuzz-artifacts
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ Thumbs.db
# Generated
*.o
*.a
__pycache__/
*.pyc

# Flatpak
.flatpak-builder/
Expand Down
97 changes: 92 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ sudo apt-get install -y rustc cargo pkg-config libgtk-4-dev \
libadwaita-1-dev libdav1d-dev libssl-dev gettext
```

Rust mínimo: **1.83** (definido em `rust-toolchain.toml`; `rustup`
respeita automaticamente).
Rust mínimo: **1.92** (declarado no `Cargo.toml`; `rust-toolchain.toml` seleciona stable).

## Compilando

Expand All @@ -42,8 +41,8 @@ cargo build --release --features "gui ai" -p bigiris
# Workspace inteiro (headless):
cargo test --workspace --all-targets --locked

# GUI + IA (precisa das libs de sistema acima):
cargo test -p bigiris --features "gui ai" --all-targets --locked
# GUI + IA em todo o workspace (precisa das libs de sistema acima):
cargo test --workspace --all-features --all-targets --locked

# Self-test do binário:
./target/release/bigiris --self-test
Expand All @@ -54,12 +53,72 @@ cargo test -p bigiris --features "gui ai" --all-targets --locked
```bash
cargo fmt --all
cargo clippy --workspace --all-targets --locked -- -D warnings
cargo clippy -p bigiris --features "gui ai" --all-targets --locked -- -D warnings
cargo clippy --workspace --all-features --all-targets --locked -- -D warnings
```

Os 3 são gates obrigatórios no CI (`.github/workflows/ci.yml`).
Warnings viram erro, então PRs com clippy sujo não passam.

## Campanha de qualidade no GNOME

O runner preserva logs, comandos, códigos de saída e duração de cada verificação
em `target/quality/`. Falhas permanecem no histórico; uma nova execução gera
novas evidências. A matriz e os resultados medidos estão em
[docs/QUALITY.md](docs/QUALITY.md).

```bash
# Contratos Rust/Python, Clippy e arquivos de integração:
python3 scripts/quality.py --gui

# Na sessão GNOME, inclui abrir e destruir janelas com imagens sintéticas:
python3 scripts/quality.py --gui --graphical

# Benchmark release, 64 conversões concorrentes e estabilidade por 5 minutos:
python3 scripts/quality.py --gui --graphical --performance --soak-seconds 300

# Instala o auditor apenas no diretório de build deste checkout:
cargo install --locked cargo-audit --root target/quality/tools
python3 scripts/quality.py --gui --audit

# Depois de obter dependências e atualizar a base RustSec, repetição offline:
python3 scripts/quality.py --gui --graphical --audit --offline
```

O runner também exige `desktop-file-validate`, `appstreamcli`,
`glib-compile-schemas` e `msgfmt`. `--graphical` exige Wayland/X11 disponível;
no CI, usamos Xvfb e uma sessão D-Bus isolada. O backend GSettings é de memória
durante os testes gráficos, para preservar as preferências pessoais. O teste
recusa execução direta sem `GSETTINGS_BACKEND=memory`. Inclui preservação do
ponto do cursor no zoom por geometria real e pixels renderizados, além de
cancelamento na navegação e liberação de estado no fechamento.

A suíte CLI com a feature `ai` testa recusa sob RLIMIT_AS de 4 GiB antes do
download, sem cache/saída e com original preservado. Inferência real exige
8 GiB de RAM disponível e modelo na cache isolada; use `scripts/ai_smoke.py`
e consulte os limites em `docs/QUALITY.md`.

Capturas opcionais registram somente os widgets das janelas sintéticas:

```bash
BIGIRIS_TEST_SCREENSHOTS_DIR="$PWD/target/quality/screenshots" \
BIGIRIS_TEST_COLOR_SCHEME=light \
python3 scripts/quality.py --gui --graphical
```

Repita com `BIGIRIS_TEST_COLOR_SCHEME=dark` para o tema escuro. Para Memcheck,
compile o probe e use símbolos de depuração compatíveis com a glibc instalada:

```bash
cargo build -p bigimage-core --example quality_probe --locked
valgrind --tool=memcheck --leak-check=full \
--errors-for-leak-kinds=definite,indirect --error-exitcode=99 \
./target/debug/examples/quality_probe soak 1 320x240
```

O probe mede o núcleo com o allocator padrão; o aplicativo usa mimalloc.
Estabilidade de RSS e liberação das referências GTK são verificações diferentes
de Memcheck e não demonstram, isoladamente, ausência de vazamentos.

## Estrutura

```
Expand Down Expand Up @@ -128,3 +187,31 @@ etc.) traduzem a partir do pt-BR.
## Reportando vulnerabilidades

Não abra PR/issue pública. Veja [SECURITY.md](SECURITY.md).

### Fuzzing e estabilidade prolongada

`python3 scripts/fuzz.py --seconds 120` gera somente imagens sintéticas e
instrumenta o core com ASan/LSan e cobertura LLVM. Use Rust nightly. Em hosts
que fornecem Rust pelo sistema, `--allow-bootstrap` habilita explicitamente
as opções experimentais só nesse build de desenvolvimento; o release não
usa essas opções. O relatório fica em `target/quality/fuzz/summary.json`.
A campanha limita a entrada mutada a 16 KiB e o decode completo a um milhão
de pixels; cabeçalhos maiores continuam inspecionados. Testes de limites e
o sandbox cobrem rejeições fora desse orçamento. Bibliotecas nativas ligadas
externamente não são instrumentadas. Valgrind continua sendo um gate
independente no CI.

`python3 scripts/release_smoke.py` verifica o sandbox e os 13 formatos no
release. `python3 scripts/ai_smoke.py` executa inferência ONNX real em duas
imagens sintéticas com transparência, usando somente o cache isolado em
`target/quality/ai/`. O primeiro download exige `--allow-download`; o teste
não toca no cache pessoal e registra tempo, RAM e preservação de RGB/alpha.

Após `cargo build --release -p bigimage-core --example quality_probe --locked`,
`python3 scripts/soak.py --seconds 28800 --background` executa oito horas do
probe em processo persistente. Consulte `target/quality/long-soak/status.json`:
`running` significa pendente, nunca aprovação. O probe exercita o core;
soak de sessão GNOME, GPU, slideshow e acessibilidade exige validação própria.

Para distribuir a candidata, confira `docs/RELEASE-CANDIDATE.md`. Instalação,
associação MIME padrão e publicação são ações diferentes da criação do pacote.
Loading
Loading