zupt/.github/workflows/ci.yml
Cristian Cezar Moisés 5050570b23 v5.0.0: version bump, audit fixes, documentation overhaul
Version bumped to 5.0.0 across include/zupt.h, all packaging recipes, man
page, and docs.

Audit fixes (pre-5.0.0 review):
- src/zupt_format.c: overflow-safe bound in the solid-mode `test` path
  (off+sz could wrap and drive an OOB read in zupt_xxh64 on a crafted archive;
  the extract path was already hardened, the test path was not).
- gui: run_async now marshals the completion callback onto the GUI thread with
  QueuedConnection (a bare functor connected DirectConnection and touched
  widgets off the worker thread); Extract auto-detect note survives the log
  clear via a new `info` param.
- .github/workflows/ci.yml: trigger on `master` (was main/develop, so CI never
  ran); `make dist` tarball is vaptvupt-*.tar.gz not zupt-*; the ASAN PQ
  round-trip uses native --pq (was --pq-sdk, which fails on the source-only
  build and blocked the release job).

Documentation:
- New AUDIT.md (methodology, FIPS 203 conformance validation, findings, repro).
- CHANGELOG 5.0.0 entry covers the FIPS 203 conformance fix + BREAKING note and
  the GUI/CLI/security/packaging work.
- README "What's new in 5.0.0", download tables (incl. Windows/macOS/BSD +
  portable GUI), version-history row.
- SECURITY.md + THREAT_MODEL.md: ML-KEM-768 documented as FIPS 203, validated
  byte-for-byte against OpenSSL 3.5.
- Accuracy fixes: man page (--kdf default is PBKDF2 on source-only; codec
  2.60.4), rpm %description, debian control/copyright, homebrew header
  (no vendored library on source-only builds).

make check 16/16 (FIPS 203 conformance 3/3, all distro-safe checks).
2026-07-10 17:22:02 -03:00

254 lines
9.1 KiB
YAML

# SPDX-License-Identifier: AGPL-3.0-or-later
# Copyright (c) 2025-2026 Cristian Cezar Moisés
#
# Zupt CI matrix.
#
# Mirrors the project's local-verification protocol from PROMPT.md §6:
# 1. Plain GCC build
# 2. Plain Clang build
# 3. Strict GCC (full warning set)
# 4. Strict Clang (full warning set)
# 5. ASAN + UBSAN
# 6. Full regression suite (12 suites: audit, dedup, path-traversal,
# argument-order, block-swap, F-08, F-09 byte sweep, F-10, F-11,
# F-12, packaging syntax, dist reproducibility)
# 7. License header audit
# 8. `make dist` reproducibility (two runs, sha256 must match)
# 9. aarch64 cross-test via QEMU emulation
# 10. Automatic release on git tag push
name: CI
on:
push:
branches: [master]
tags: ['v*']
pull_request:
branches: [master]
jobs:
# ─── Plain build + test, exactly as a user would do it ───
build-and-test:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
cc: [gcc, clang]
steps:
- uses: actions/checkout@v4
- name: Install build deps
run: |
sudo apt-get update
sudo apt-get install -y build-essential clang dpkg-dev python3
- name: Build (${{ matrix.cc }})
run: make CC=${{ matrix.cc }} -j$(nproc)
- name: zupt version
run: ./zupt version
- name: Full regression suite
run: make test
- name: License header audit
run: make audit-licenses
# ─── Strict warning matrix — what the project's §6 protocol uses ───
strict-warnings:
runs-on: ubuntu-24.04
strategy:
fail-fast: false
matrix:
include:
- cc: gcc
cflags: "-Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -Wformat-security -Wlogical-op -Wjump-misses-init -Wdouble-promotion -O2 -std=c11 -Werror"
- cc: clang
cflags: "-Wall -Wextra -Wpedantic -Wshadow -Wcast-align -Wstrict-prototypes -Wmissing-prototypes -Wnull-dereference -O2 -std=c11 -Werror"
steps:
- uses: actions/checkout@v4
- name: Install build deps
run: sudo apt-get update && sudo apt-get install -y build-essential clang
- name: Strict ${{ matrix.cc }} build (warnings → errors)
run: make CC=${{ matrix.cc }} CFLAGS="${{ matrix.cflags }}" -j$(nproc)
# ─── ASAN + UBSAN — catches memory bugs the warning matrix can't ───
sanitizers:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install build deps
run: sudo apt-get update && sudo apt-get install -y build-essential python3
- name: Build with ASAN + UBSAN
run: make test-asan
- name: Native --pq byte-exact roundtrip under ASAN
env:
ASAN_OPTIONS: detect_leaks=0:abort_on_error=1
UBSAN_OPTIONS: print_stacktrace=1:halt_on_error=1
run: |
# Native hybrid ML-KEM-768 + X25519 (the source-only default; --pq-sdk
# needs a WITH_SDK=1 build and is unavailable here).
./zupt_asan keygen -o /tmp/k.priv
./zupt_asan keygen --pub -o /tmp/k.pub -k /tmp/k.priv
./zupt_asan compress --pq /tmp/k.pub /tmp/a.zupt include/
mkdir -p /tmp/extracted
./zupt_asan extract --pq /tmp/k.priv -o /tmp/extracted /tmp/a.zupt
diff -qr include /tmp/extracted/include
# ─── PIE hardening build — verifies no runtime breakage from -fPIE ───
pie-hardening:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install build deps
run: sudo apt-get update && sudo apt-get install -y build-essential
- name: Build with PIE + hardening
run: |
make CFLAGS="-O2 -std=c11 -fPIE -fstack-protector-strong -D_FORTIFY_SOURCE=2 -Wformat -Wformat-security" \
LDFLAGS="-pie -Wl,-z,relro -Wl,-z,now -Wl,-z,noexecstack" \
-j$(nproc)
- name: Verify binary is PIE
run: |
file ./zupt | grep -E "ELF .*executable.*pie|ELF .*shared object" || \
{ file ./zupt; echo "binary is not PIE"; exit 1; }
- name: Smoke test
run: |
echo "test" > /tmp/in.txt
./zupt c -p secret /tmp/a.zupt /tmp/in.txt
mkdir /tmp/out
(cd /tmp/out && ./../../home/runner/work/zupt/zupt/zupt x -p secret /tmp/a.zupt) || \
{ cd /tmp/out && "$GITHUB_WORKSPACE/zupt" x -p secret /tmp/a.zupt; }
diff -q /tmp/in.txt /tmp/out/in.txt
# ─── aarch64 cross-build via QEMU emulation ───
cross-aarch64:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
platforms: arm64
- name: Build + test inside aarch64 container
run: |
docker run --rm --platform linux/arm64 \
-v "$PWD":/src -w /src \
ubuntu:24.04 \
bash -c '
apt-get update -qq
apt-get install -y -qq build-essential python3
make -j$(nproc)
./zupt version
make test
'
# ─── make dist reproducibility ───
dist-reproducibility:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install build deps
run: sudo apt-get update && sudo apt-get install -y build-essential python3
- name: First dist build
run: make dist
- name: Capture sha256 (run 1)
id: sha1
run: |
VER=$(grep '^#define ZUPT_VERSION_STRING' include/zupt.h | awk -F'"' '{print $2}')
SHA=$(sha256sum /tmp/vaptvupt-$VER.tar.gz | awk '{print $1}')
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
echo "ver=$VER" >> "$GITHUB_OUTPUT"
echo "Run 1: $SHA"
- name: Second dist build (must produce identical sha256)
run: make dist
- name: Verify reproducibility
run: |
VER="${{ steps.sha1.outputs.ver }}"
SHA2=$(sha256sum /tmp/vaptvupt-$VER.tar.gz | awk '{print $1}')
if [ "$SHA2" != "${{ steps.sha1.outputs.sha }}" ]; then
echo "::error::make dist is NOT reproducible"
echo " run 1: ${{ steps.sha1.outputs.sha }}"
echo " run 2: $SHA2"
exit 1
fi
echo "Reproducible ✓ ($SHA2)"
- name: Upload reproducible source tarball
uses: actions/upload-artifact@v4
with:
name: zupt-source-tarball
path: /tmp/vaptvupt-*.tar.gz
# ─── Packaging-recipe syntax (cross-distro) ───
packaging-syntax:
runs-on: ubuntu-24.04
steps:
- uses: actions/checkout@v4
- name: Install validators
run: |
sudo apt-get update
sudo apt-get install -y build-essential dpkg-dev ruby rpm
- name: Build (for include/zupt.h to exist; not strictly needed for syntax test)
run: make -j$(nproc)
- name: Run packaging syntax test
run: bash tests/test_packaging_syntax.sh
# ─── Automatic GitHub release on git tag push ───
release:
if: startsWith(github.ref, 'refs/tags/v')
needs: [build-and-test, strict-warnings, sanitizers, dist-reproducibility, packaging-syntax]
runs-on: ubuntu-24.04
permissions:
contents: write
steps:
- uses: actions/checkout@v4
- name: Install build deps
run: sudo apt-get update && sudo apt-get install -y build-essential python3
- name: Build reproducible source tarball
run: make dist
- name: Get version
id: ver
run: |
VER=$(grep '^#define ZUPT_VERSION_STRING' include/zupt.h | awk -F'"' '{print $2}')
echo "version=$VER" >> "$GITHUB_OUTPUT"
- name: Verify tag matches version
run: |
TAG="${GITHUB_REF#refs/tags/}"
EXPECTED="v${{ steps.ver.outputs.version }}"
if [ "$TAG" != "$EXPECTED" ]; then
echo "::error::tag $TAG doesn't match include/zupt.h $EXPECTED"
exit 1
fi
- name: Compute sha256
id: sha
run: |
VER="${{ steps.ver.outputs.version }}"
SHA=$(sha256sum /tmp/vaptvupt-$VER.tar.gz | awk '{print $1}')
echo "sha=$SHA" >> "$GITHUB_OUTPUT"
echo "$SHA vaptvupt-$VER.tar.gz" > /tmp/vaptvupt-$VER.tar.gz.sha256
- name: Create GitHub release
uses: softprops/action-gh-release@v2
with:
files: |
/tmp/vaptvupt-${{ steps.ver.outputs.version }}.tar.gz
/tmp/vaptvupt-${{ steps.ver.outputs.version }}.tar.gz.sha256
body: |
## Zupt v${{ steps.ver.outputs.version }}
Reproducible source tarball.
```
sha256: ${{ steps.sha.outputs.sha }}
```
See CHANGELOG.md for release notes.
### Verifying the tarball
```sh
sha256sum -c zupt-${{ steps.ver.outputs.version }}.tar.gz.sha256
```
### Building
```sh
tar xzf zupt-${{ steps.ver.outputs.version }}.tar.gz
cd zupt-${{ steps.ver.outputs.version }}
make
make test
sudo make install
```