release: prepare ZUPT 5.2.6 native gates

This commit is contained in:
Cristian Cezar Moisés 2026-08-31 18:12:27 -03:00
commit 7821523942
31 changed files with 418 additions and 207 deletions

View file

@ -11,7 +11,7 @@ on:
required: true required: true
type: number type: number
tag: tag:
description: Existing annotated release tag, for example v5.2.5 description: Existing annotated release tag, for example v5.2.6
required: true required: true
type: string type: string

View file

@ -1,12 +1,12 @@
<!-- SPDX-License-Identifier: AGPL-3.0-or-later --> <!-- SPDX-License-Identifier: AGPL-3.0-or-later -->
# ZUPT 5.2.5 audit guide and finding history # ZUPT 5.2.6 audit guide and finding history
This document describes review surfaces and reproducible checks. It is an This document describes review surfaces and reproducible checks. It is an
upstream self-review, not an independent audit, certification, or guarantee. upstream self-review, not an independent audit, certification, or guarantee.
`SECURITY.md` defines reporting policy and `THREAT_MODEL.md` defines the `SECURITY.md` defines reporting policy and `THREAT_MODEL.md` defines the
security boundary. security boundary.
## 5.2.5 scope ## 5.2.6 scope
The baseline scope is the source-only CLI and its bundled source codec: The baseline scope is the source-only CLI and its bundled source codec:
@ -28,7 +28,7 @@ output.
## Source-only review ## Source-only review
The 5.2.5 baseline retains the source-only boundary introduced in 5.2.2, which The 5.2.6 baseline retains the source-only boundary introduced in 5.2.2, which
removed incomplete SDK/PQBOX header snapshots and local precompiled-library removed incomplete SDK/PQBOX header snapshots and local precompiled-library
expectations. Git and new upstream source expectations. Git and new upstream source
archives are intended to contain no compiled executable, object, shared/static archives are intended to contain no compiled executable, object, shared/static
@ -42,10 +42,10 @@ scripts/check-source-only.sh
# committed Git tree or immutable tag # committed Git tree or immutable tag
scripts/check-source-only.sh --tag HEAD scripts/check-source-only.sh --tag HEAD
scripts/check-source-only.sh --tag v5.2.5 scripts/check-source-only.sh --tag v5.2.6
# generated source archive # generated source archive
scripts/check-source-only.sh --archive /path/to/zupt-5.2.5.tar.gz scripts/check-source-only.sh --archive /path/to/zupt-5.2.6.tar.gz
``` ```
The scanner checks extensions and magic bytes, nested archives, symlink targets, The scanner checks extensions and magic bytes, nested archives, symlink targets,
@ -109,7 +109,7 @@ without evidence.
The following upstream self-audit results apply only to the 5.2.2 candidate at The following upstream self-audit results apply only to the 5.2.2 candidate at
commit `ff99770` on the recorded local Linux environments. The immutable 5.2.2 commit `ff99770` on the recorded local Linux environments. The immutable 5.2.2
tag was not promoted after post-tag CI integration failures. These results are tag was not promoted after post-tag CI integration failures. These results are
not independent certification, a 5.2.5 result, or evidence that release assets not independent certification, a 5.2.6 result, or evidence that release assets
were published. were published.
| Gate | Result | Recorded evidence | | Gate | Result | Recorded evidence |
@ -141,9 +141,33 @@ A separate local openSUSE Tumbleweed reproduction resolved the explicit
produced exactly one `zupt-5.2.4.tar.gz`, which passed the source-only scanner. produced exactly one `zupt-5.2.4.tar.gz`, which passed the source-only scanner.
This isolates a release/test harness defect; it is not evidence of a product, This isolates a release/test harness defect; it is not evidence of a product,
archive-format, cryptographic, codec, or SDK ABI change. It also does not turn archive-format, cryptographic, codec, or SDK ABI change. It also does not turn
the skipped native jobs into passes or transfer any result to 5.2.5. the skipped native jobs into passes or transfer any result to 5.2.6.
The exact 5.2.5 candidate must repeat the required suite. Native Windows and ## Prior 5.2.5 exact-tag native-gate evidence
The immutable `v5.2.5` candidate was not promoted. Exact-tag GitHub Actions run
`33434986357` completed 13 jobs successfully, while its native Windows and
macOS jobs failed. The Windows regression did not preserve every requested
hostile path byte across its command-line boundary. The macOS gate exposed both
an unavailable `explicit_bzero` assumption and Bash 3.2 empty-array behavior in
the source scanner exercised by `make check`.
The 5.2.6 corrections select the existing compiler-resistant volatile wipe on
Darwin and NetBSD, guard every relevant scanner array, and make the Windows
fixture accept explicit hexadecimal bytes, verify the full requested path in
the archive, and reject each dangerous raw byte fragment anywhere in diagnostic
output. These changes do not alter the archive format, cryptography, bundled
codec, or SDK ABI. They are proposed corrections, not proof that any 5.2.6
native or hosted gate has passed.
A separate local compatibility run executed the corrected scanner with genuine
GNU Bash 3.2.57 in a clean clone. All four exercised modes completed: the
repository audit reported 609 files and one archive; `--tree` reported 204/0;
`--archive` reported 201/1; and `--root` plus `--tag v5.2.5` reported 810/2.
This is targeted scanner compatibility evidence only, not exact-v5.2.6 hosted
CI, package, native-platform, or promotion evidence.
The exact 5.2.6 candidate must repeat the required suite. Native Windows and
macOS gates, hosted GitHub CI and release promotion, authenticated OBS macOS gates, hosted GitHub CI and release promotion, authenticated OBS
validation, and resolution of the openSUSE automatic `debugsource` rpmlint validation, and resolution of the openSUSE automatic `debugsource` rpmlint
`no-binary` finding remain pending until recorded otherwise. `no-binary` finding remain pending until recorded otherwise.
@ -165,7 +189,7 @@ AES implementation has documented cache-timing risk on hostile shared hardware.
The following entries are retained as release history. Their regression tests The following entries are retained as release history. Their regression tests
should be rerun, but the historical resolution does not itself constitute a should be rerun, but the historical resolution does not itself constitute a
5.2.5 test result. 5.2.6 test result.
| First corrected | Severity | Finding | Resolution recorded at the time | | First corrected | Severity | Finding | Resolution recorded at the time |
|---|---|---|---| |---|---|---|---|
@ -206,12 +230,12 @@ include SHA-256 checksums. The gated GUI set adds the architecture-independent
DEB, noarch/source RPM, and source-only portable GUI ZIP. Package gates include DEB, noarch/source RPM, and source-only portable GUI ZIP. Package gates include
exact payload/dependency and installed off-screen integration checks; the exact payload/dependency and installed off-screen integration checks; the
portable ZIP additionally receives source scans, an exact safe-member allowlist, portable ZIP additionally receives source scans, an exact safe-member allowlist,
and an extracted launcher test. An AppImage is not promoted by the 5.2.5 and an extracted launcher test. An AppImage is not promoted by the 5.2.6
policy; AppDir and Flatpak bundles, GUI platform installers, and bare policy; AppDir and Flatpak bundles, GUI platform installers, and bare
Linux/Windows executables are also excluded. Windows ZIP and macOS DMG outputs Linux/Windows executables are also excluded. Windows ZIP and macOS DMG outputs
remain CLI-only. remain CLI-only.
No Wine result is retained as release evidence for 5.2.5. Cross-compilation No Wine result is retained as release evidence for 5.2.6. Cross-compilation
does not establish native-Windows behavior. Extended-length/device namespace does not establish native-Windows behavior. Extended-length/device namespace
paths, raw UNC output roots, and mapped/network-drive output are unsupported; paths, raw UNC output roots, and mapped/network-drive output are unsupported;
the native Windows workflow remains a publication gate for the ZIP containing the native Windows workflow remains a publication gate for the ZIP containing

View file

@ -1,5 +1,31 @@
# ZUPT Changelog # ZUPT Changelog
## [5.2.6] — 2026-08-31 — Native release-gate portability corrections
Corrective successor to the immutable, unpromoted `v5.2.5` candidate. Exact-tag
GitHub Actions run `33434986357` completed 13 jobs successfully, but the native
Windows and macOS jobs failed, so no 5.2.5 assets were promoted. The tag and its
recorded evidence remain unchanged.
- Use the compiler-resistant volatile wipe fallback on macOS and NetBSD instead
of assuming that their C libraries export `explicit_bzero`; supported glibc,
FreeBSD, and OpenBSD paths retain their existing selection.
- Make the source-only scanner's empty-array handling compatible with the
system Bash 3.2 shipped by macOS, including repository, tag, standalone-tree,
standalone-archive, and path-component traversal paths.
- Make hostile archive-path fixtures accept explicit hexadecimal bytes, verify
the requested path bytes in the generated archive, and reject the dangerous
raw byte fragment anywhere in diagnostic output, so the Windows regression
does not depend on command-line conversion or benign path prefixes.
- Carry the 5.2.5 source-only and security baseline forward without changing
the archive format, cryptography, bundled codec release, or SDK ABI.
- Realign current package, workflow, artifact, and tag references to 5.2.6;
leave release-archive checksums explicitly pending until the final source
archive is generated.
- Require fresh exact-`v5.2.6` source, checksum, hosted CI, native-platform,
package, OBS, and promotion evidence. No prior candidate result transfers
automatically, and this changelog entry does not claim those gates passed.
## [5.2.5] — 2026-08-31 — OBS service working-directory correction ## [5.2.5] — 2026-08-31 — OBS service working-directory correction
Corrective successor to the immutable `v5.2.4` candidate. GitHub Actions run Corrective successor to the immutable `v5.2.4` candidate. GitHub Actions run

View file

@ -1,4 +1,4 @@
# Distributing ZUPT 5.2.5 # Distributing ZUPT 5.2.6
This document describes the packaging material maintained in the ZUPT This document describes the packaging material maintained in the ZUPT
source repository. A recipe in `packaging/` is not evidence that a package has source repository. A recipe in `packaging/` is not evidence that a package has
@ -14,15 +14,19 @@ https://github.com/cristiancmoises/zupt
GitHub is the canonical source and release host. Packaging must never fetch GitHub is the canonical source and release host. Packaging must never fetch
`zupt-web` or substitute an asset from another project. `zupt-web` or substitute an asset from another project.
The `v5.2.2`, `v5.2.3`, and `v5.2.4` tags are immutable non-promoted candidates. The `v5.2.2`, `v5.2.3`, `v5.2.4`, and `v5.2.5` tags are immutable
non-promoted candidates.
The v5.2.3 source-policy test assumed LF for a Windows `.bat` file that Git The v5.2.3 source-policy test assumed LF for a Windows `.bat` file that Git
correctly checks out as CRLF. Exact-tag GitHub Actions run `33431386002` then correctly checks out as CRLF. Exact-tag GitHub Actions run `33431386002` then
recorded 12 successful v5.2.4 jobs, one openSUSE service-harness failure caused recorded 12 successful v5.2.4 jobs, one openSUSE service-harness failure caused
by its working directory, and skipped dependent Windows/macOS jobs. A local by its working directory, and skipped dependent Windows/macOS jobs. A local
Tumbleweed reproduction confirmed that `refs/tags/v5.2.4` is valid and that Tumbleweed reproduction confirmed that `refs/tags/v5.2.4` is valid and that
entering the service directory completes the source-service chain. Corrective entering the service directory completes the source-service chain. Corrective
packages and release assets must use `v5.2.5`; never move or overwrite an working-directory integration was carried by v5.2.5, whose exact-tag GitHub
earlier tag or checksum, and never transfer prior evidence automatically. Actions run `33434986357` completed 13 jobs successfully but failed the native
Windows and macOS jobs. Corrective packages and release assets must use
`v5.2.6`; never move or overwrite an earlier tag or checksum, and never transfer
prior evidence automatically.
This corrective version changes release/test integration only; the product, This corrective version changes release/test integration only; the product,
archive format, cryptography, codec, and SDK ABI remain unchanged. archive format, cryptography, codec, and SDK ABI remain unchanged.
@ -54,7 +58,7 @@ Audit the current tree or a generated archive with:
```sh ```sh
scripts/check-source-only.sh scripts/check-source-only.sh
scripts/check-source-only.sh --archive /path/to/zupt-5.2.5.tar.gz scripts/check-source-only.sh --archive /path/to/zupt-5.2.6.tar.gz
``` ```
The scanner reports paths, not file contents, and exits nonzero on a violation. The scanner reports paths, not file contents, and exits nonzero on a violation.
@ -68,8 +72,8 @@ the commit omits Git's commit-ID PAX header:
```sh ```sh
SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)" \ SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)" \
make DIST_TARBALL=/tmp/zupt-5.2.5.tar.gz dist make DIST_TARBALL=/tmp/zupt-5.2.6.tar.gz dist
sha256sum /tmp/zupt-5.2.5.tar.gz sha256sum /tmp/zupt-5.2.6.tar.gz
``` ```
With identical committed input and `SOURCE_DATE_EPOCH`, repeated exports must With identical committed input and `SOURCE_DATE_EPOCH`, repeated exports must
@ -115,12 +119,12 @@ private-library RPATH.
| openSUSE / OBS | `packaging/opensuse/` | source and binary RPM through OBS | | openSUSE / OBS | `packaging/opensuse/` | source and binary RPM through OBS |
| Debian / Ubuntu | `packaging/debian/`, `packaging/build-deb.sh` | Debian metadata and binary DEB after the target gate | | Debian / Ubuntu | `packaging/debian/`, `packaging/build-deb.sh` | Debian metadata and binary DEB after the target gate |
| RPM release artifact | `packaging/opensuse/zupt.spec`, `packaging/build-rpm.sh` | source and binary RPM after the target gate | | RPM release artifact | `packaging/opensuse/zupt.spec`, `packaging/build-rpm.sh` | source and binary RPM after the target gate |
| GUI DEB | `packaging/build-gui-deb.sh` | `zupt-gui_5.2.5_all.deb` after payload/dependency and installed integration gates | | GUI DEB | `packaging/build-gui-deb.sh` | `zupt-gui_5.2.6_all.deb` after payload/dependency and installed integration gates |
| GUI RPM | `packaging/build-gui-rpm.sh` | `zupt-gui-5.2.5-1.noarch.rpm` and matching `.src.rpm` after package and installed integration gates | | GUI RPM | `packaging/build-gui-rpm.sh` | `zupt-gui-5.2.6-1.noarch.rpm` and matching `.src.rpm` after package and installed integration gates |
| Linux CLI archive | `.github/workflows/ci.yml` | `zupt-5.2.5-linux-x86_64.tar.xz` with notices after dependency, member, and extracted functional gates | | Linux CLI archive | `.github/workflows/ci.yml` | `zupt-5.2.6-linux-x86_64.tar.xz` with notices after dependency, member, and extracted functional gates |
| Portable GUI source | `packaging/portable/`, `.github/workflows/ci.yml` | `zupt-gui-5.2.5-portable.zip` after source scan, member allowlist, and extracted off-screen integration gate | | Portable GUI source | `packaging/portable/`, `.github/workflows/ci.yml` | `zupt-gui-5.2.6-portable.zip` after source scan, member allowlist, and extracted off-screen integration gate |
| Fedora / RPM-based systems | `packaging/rpm/zupt.spec` | downstream RPM starting point | | Fedora / RPM-based systems | `packaging/rpm/zupt.spec` | downstream RPM starting point |
| AppImage helper | `packaging/build-appimage.sh` | downstream-only helper; no 5.2.5 AppImage is promoted | | AppImage helper | `packaging/build-appimage.sh` | downstream-only helper; no 5.2.6 AppImage is promoted |
| Windows | `.github/workflows/cross-platform.yml` | native ZIP (executable plus notices) after the required native gate | | Windows | `.github/workflows/cross-platform.yml` | native ZIP (executable plus notices) after the required native gate |
| macOS | `packaging/build-dmg.sh` | native-architecture DMG after the native gate | | macOS | `packaging/build-dmg.sh` | native-architecture DMG after the native gate |
| Arch Linux | `packaging/aur/PKGBUILD` | AUR package recipe | | Arch Linux | `packaging/aur/PKGBUILD` | AUR package recipe |
@ -196,17 +200,17 @@ expectations, then test the installed launcher off-screen against the matching
### Portable and native release artifacts ### Portable and native release artifacts
The Linux x86_64 gate packages the tested `zupt` executable as The Linux x86_64 gate packages the tested `zupt` executable as
`zupt-5.2.5-linux-x86_64.tar.xz` beside README, changelog, security guidance, `zupt-5.2.6-linux-x86_64.tar.xz` beside README, changelog, security guidance,
and every applicable public license and notice. Its dynamic-library allowlist, and every applicable public license and notice. Its dynamic-library allowlist,
archive member allowlist, and extracted CLI functional suite must pass. archive member allowlist, and extracted CLI functional suite must pass.
The `zupt-gui-5.2.5-portable.zip` artifact is source-only: it contains the GUI The `zupt-gui-5.2.6-portable.zip` artifact is source-only: it contains the GUI
Python source, shell/macOS/Windows launchers, icons, provenance, changelog, and Python source, shell/macOS/Windows launchers, icons, provenance, changelog, and
licenses, but no Python, Qt, CLI, or compiled runtime. The gate scans both the licenses, but no Python, Qt, CLI, or compiled runtime. The gate scans both the
assembled and extracted trees, verifies an exact safe member allowlist, and assembled and extracted trees, verifies an exact safe member allowlist, and
runs the extracted launcher off-screen against the tested CLI. runs the extracted launcher off-screen against the tested CLI.
AppImage creation is deliberately offline and is not a 5.2.5 release gate. AppImage creation is deliberately offline and is not a 5.2.6 release gate.
Supply a locally verified `appimagetool`, type-2 runtime, and the complete Supply a locally verified `appimagetool`, type-2 runtime, and the complete
license/source-relink compliance notice for those exact runtime bytes; the license/source-relink compliance notice for those exact runtime bytes; the
helper never downloads any input: helper never downloads any input:
@ -222,7 +226,7 @@ APPIMAGE_RUNTIME_COMPLIANCE_FILE=/verified/path/runtime-compliance.txt \
The runtime inspected while preparing 5.2.2 omitted a linked component from The runtime inspected while preparing 5.2.2 omitted a linked component from
its notice and did not provide the complete LGPL source/relink handoff required its notice and did not provide the complete LGPL source/relink handoff required
by this release policy. No AppImage produced by this helper is promoted by the by this release policy. No AppImage produced by this helper is promoted by the
upstream 5.2.5 workflow. AppDir and Flatpak bundles and GUI platform installers upstream 5.2.6 workflow. AppDir and Flatpak bundles and GUI platform installers
are also excluded. Bare Linux and Windows executables are not promoted; their are also excluded. Bare Linux and Windows executables are not promoted; their
CLI programs appear only inside notice-bearing archives. The Windows ZIP and CLI programs appear only inside notice-bearing archives. The Windows ZIP and
macOS DMG remain CLI-only. macOS DMG remain CLI-only.
@ -237,8 +241,8 @@ DIST_DIR="$release_dir" RUN_CHECKS=1 packaging/build-dmg.sh
The Windows ZIP (including its executable and notices) must be built and tested The Windows ZIP (including its executable and notices) must be built and tested
by the Windows job in `.github/workflows/cross-platform.yml`; it is not a by the Windows job in `.github/workflows/cross-platform.yml`; it is not a
cross-compiled release claim from a Linux build. No Wine result is retained as cross-compiled release claim from a Linux build. No Wine result is retained as
5.2.5 release evidence. Extended-length/device namespace paths, raw UNC output 5.2.6 release evidence. Extended-length/device namespace paths, raw UNC output
roots, and mapped/network-drive output are not supported in 5.2.5. Publish the roots, and mapped/network-drive output are not supported in 5.2.6. Publish the
exact architecture recorded by the native job. exact architecture recorded by the native job.
These helpers create binary distribution artifacts for the release page, not These helpers create binary distribution artifacts for the release page, not
content to be committed to Git or included in the source archive. content to be committed to Git or included in the source archive.
@ -246,7 +250,7 @@ content to be committed to Git or included in the source archive.
### AUR, Homebrew, Guix, and Nix ### AUR, Homebrew, Guix, and Nix
After calculating the final reproducible source archive, but before creating or After calculating the final reproducible source archive, but before creating or
publishing the immutable tag, update each recipe to version 5.2.5 and to the publishing the immutable tag, update each recipe to version 5.2.6 and to the
exact digest or content hash expected by its package manager. These recipe exact digest or content hash expected by its package manager. These recipe
directories are excluded from the source archive, so this does not create a directories are excluded from the source archive, so this does not create a
checksum cycle. Commit the pinned recipes in the tagged tree, then build and checksum cycle. Commit the pinned recipes in the tagged tree, then build and
@ -265,7 +269,7 @@ build.
For every published artifact: For every published artifact:
1. start from the immutable `v5.2.5` tag; 1. start from the immutable `v5.2.6` tag;
2. keep `WITH_SDK=0 WITH_PQBOX=0` unless system dependencies are declared; 2. keep `WITH_SDK=0 WITH_PQBOX=0` unless system dependencies are declared;
3. record the exact OS, distribution release, architecture, and toolchain; 3. record the exact OS, distribution release, architecture, and toolchain;
4. run format validation plus installed `--version`, `--help`, and archive 4. run format validation plus installed `--version`, `--help`, and archive
@ -283,7 +287,7 @@ than redirecting consumers to an unverified file.
## Downstream checklist ## Downstream checklist
- [ ] The source URL resolves to the immutable `v5.2.5` tag. - [ ] The source URL resolves to the immutable `v5.2.6` tag.
- [ ] The source archive passes `scripts/check-source-only.sh --archive`. - [ ] The source archive passes `scripts/check-source-only.sh --archive`.
- [ ] The recipe checksum matches the downloaded source exactly. - [ ] The recipe checksum matches the downloaded source exactly.
- [ ] `WITH_SDK=0 WITH_PQBOX=0` is explicit, or system dependencies are complete. - [ ] `WITH_SDK=0 WITH_PQBOX=0` is explicit, or system dependencies are complete.

View file

@ -1,4 +1,4 @@
# Installing ZUPT 5.2.5 # Installing ZUPT 5.2.6
This guide covers the ZUPT command-line program and the optional Python GUI. This guide covers the ZUPT command-line program and the optional Python GUI.
The canonical source repository is The canonical source repository is
@ -24,15 +24,20 @@ service-harness job failed because its executor did not enter the service
directory, and dependent Windows/macOS jobs were skipped. A local Tumbleweed directory, and dependent Windows/macOS jobs were skipped. A local Tumbleweed
reproduction confirmed both the explicit tag ref and the corrected reproduction confirmed both the explicit tag ref and the corrected
working-directory contract. This is release/test integration only; the product, working-directory contract. This is release/test integration only; the product,
archive format, cryptography, codec, and SDK ABI are unchanged. Do not treat any archive format, cryptography, codec, and SDK ABI are unchanged. The immutable
prior candidate's artifacts or evidence as 5.2.5 packages or validation. `v5.2.5` candidate was likewise not promoted: exact-tag GitHub Actions run
`33434986357` recorded 13 successful jobs and failed native Windows/macOS jobs.
Version 5.2.6 corrects the Windows byte-exact fixture and macOS secure-wipe/Bash
3.2 portability defects, but those corrections still require fresh gates. Do
not treat any prior candidate's artifacts or evidence as 5.2.6 packages or
validation.
The 5.2.5 package set eligible for promotion after each target gate succeeds is: The 5.2.6 package set eligible for promotion after each target gate succeeds is:
| Component | Gated artifacts | | Component | Gated artifacts |
|---|---| |---|---|
| CLI | `zupt-5.2.5.tar.gz`, `zupt_5.2.5_amd64.deb`, openSUSE x86_64 binary/source RPMs, `zupt-5.2.5-linux-x86_64.tar.xz`, `zupt-5.2.5-windows-x86_64.zip`, and `ZUPT-5.2.5-macOS-*.dmg` | | CLI | `zupt-5.2.6.tar.gz`, `zupt_5.2.6_amd64.deb`, openSUSE x86_64 binary/source RPMs, `zupt-5.2.6-linux-x86_64.tar.xz`, `zupt-5.2.6-windows-x86_64.zip`, and `ZUPT-5.2.6-macOS-*.dmg` |
| GUI | `zupt-gui_5.2.5_all.deb`, `zupt-gui-5.2.5-1.noarch.rpm`, `zupt-gui-5.2.5-1.src.rpm`, and `zupt-gui-5.2.5-portable.zip` | | GUI | `zupt-gui_5.2.6_all.deb`, `zupt-gui-5.2.6-1.noarch.rpm`, `zupt-gui-5.2.6-1.src.rpm`, and `zupt-gui-5.2.6-portable.zip` |
The GUI packages require the matching `zupt` CLI package and must pass exact The GUI packages require the matching `zupt` CLI package and must pass exact
payload/dependency checks plus an installed off-screen GUI/CLI integration payload/dependency checks plus an installed off-screen GUI/CLI integration
@ -40,7 +45,7 @@ test. The source-only portable GUI ZIP bundles launchers, notices, and GUI
source, but not Python, Qt, or the CLI. The Linux tar.xz carries the tested CLI source, but not Python, Qt, or the CLI. The Linux tar.xz carries the tested CLI
beside the complete public license/notice payload. AppImage, AppDir, Flatpak beside the complete public license/notice payload. AppImage, AppDir, Flatpak
bundles, GUI platform installers, and bare Linux/Windows executables are not bundles, GUI platform installers, and bare Linux/Windows executables are not
promoted for 5.2.5. The Windows ZIP and macOS DMG contain the CLI only. Exact promoted for 5.2.6. The Windows ZIP and macOS DMG contain the CLI only. Exact
target boundaries are listed in `README.md`. target boundaries are listed in `README.md`.
The release's `SHA256SUMS` and validation notes, not the mere presence of a The release's `SHA256SUMS` and validation notes, not the mere presence of a
download link, identify an artifact that completed its gate. download link, identify an artifact that completed its gate.
@ -77,7 +82,7 @@ sudo pacman -S base-devel gzip
``` ```
Package names can differ by distribution release. These commands are examples, Package names can differ by distribution release. These commands are examples,
not a statement that 5.2.5 has been accepted into each distribution repository. not a statement that 5.2.6 has been accepted into each distribution repository.
## Build and test from source ## Build and test from source
@ -97,7 +102,7 @@ From a release archive, run the scanner as follows before extraction or from a
trusted checkout after download: trusted checkout after download:
```sh ```sh
scripts/check-source-only.sh --archive /path/to/zupt-5.2.5.tar.gz scripts/check-source-only.sh --archive /path/to/zupt-5.2.6.tar.gz
``` ```
The default build provides the native password, ML-KEM-768 + X25519 hybrid The default build provides the native password, ML-KEM-768 + X25519 hybrid

105
README.md
View file

@ -1,33 +1,44 @@
# ZUPT 5.2.5 # ZUPT 5.2.6
ZUPT is a command-line backup archiver written in C11. It combines the ZUPT is a command-line backup archiver written in C11. It combines the
bundled VaptVupt compression codec with authenticated AES-256-CTR + bundled VaptVupt compression codec with authenticated AES-256-CTR +
HMAC-SHA256 encryption, native ML-KEM-768/X25519 hybrid encryption, archive HMAC-SHA256 encryption, native ML-KEM-768/X25519 hybrid encryption, archive
integrity checks, multithreaded operation, and a Python/Qt graphical frontend. integrity checks, multithreaded operation, and a Python/Qt graphical frontend.
Version 5.2.5 corrects the working-directory contract in the exact-tag Version 5.2.6 corrects portability defects exposed by the native release gates:
openSUSE source-service harness. The immutable `v5.2.4` candidate was not macOS and NetBSD use the compiler-resistant volatile secure-wipe fallback
promoted after GitHub Actions run `33431386002`: 12 jobs succeeded, the sole instead of assuming an `explicit_bzero` symbol, the source-only scanner handles
openSUSE job failed in the standalone service executor, and its dependent empty arrays under the system Bash 3.2, and the Windows hostile-path regression
Windows and macOS jobs were skipped. A local Tumbleweed reproduction confirmed passes explicitly encoded bytes to its fixture. The immutable `v5.2.5`
that `refs/tags/v5.2.4` resolves correctly and that running the executor after candidate was not promoted after exact-tag GitHub Actions run `33434986357`:
`os.chdir(service_dir)` completes `obs_scm`, `tar`, and `recompress`. This is a 13 jobs succeeded, while the native Windows and macOS jobs failed. This is a
release/test integration correction; it does not change the archive format, release/test integration correction; it does not change the archive format,
cryptography, codec, or SDK ABI. No v5.2.4 evidence transfers automatically to cryptography, bundled codec, or SDK ABI. No v5.2.5 evidence transfers
v5.2.5. automatically to v5.2.6.
Version 5.2.2 restored the original ZUPT product name and the `zupt` command. Version 5.2.2 restored the original ZUPT product name and the `zupt` command.
The `.zupt` archive extension, format v1.6, magic bytes, codec identifiers, and The `.zupt` archive extension, format v1.6, magic bytes, codec identifiers, and
SDK ABI remain unchanged. An optional `vaptvupt` command alias may be provided SDK ABI remain unchanged. An optional `vaptvupt` command alias may be provided
for scripts written against versions 3.0.0 through 5.2.1. for scripts written against versions 3.0.0 through 5.2.1.
## Corrective changes in 5.2.5 ## Corrective changes in 5.2.6
The exact-tag openSUSE gate now executes its standalone service chain from the Darwin and NetBSD now select the secure volatile wipe fallback supported by the
directory containing `_service`. All current release paths move to 5.2.5 and existing portable implementation; scanner option/path arrays are guarded for
require fresh exact-tag hosted CI, package, native-platform, source-only, and Bash 3.2; and the Windows path fixture verifies requested bytes in the archive
checksum evidence before promotion. The `v5.2.4` tag remains immutable and while rejecting each dangerous raw byte fragment from diagnostic output. All
unpromoted. current release paths move to 5.2.6 and require fresh
exact-tag hosted CI, package, native-platform, source-only, checksum, OBS, and
promotion evidence. The `v5.2.5` tag remains immutable and unpromoted.
## Corrective changes introduced in 5.2.5
The exact-tag openSUSE gate executes its standalone service chain from the
directory containing `_service`. A local Tumbleweed reproduction confirmed
that `refs/tags/v5.2.4` resolves correctly and that entering the service
directory completes `obs_scm`, `tar`, and `recompress`. The immutable v5.2.4
candidate recorded 12 successful jobs in run `33431386002`; its openSUSE job
failed before the correction and dependent Windows/macOS jobs were skipped.
## Corrective changes introduced in 5.2.4 ## Corrective changes introduced in 5.2.4
@ -134,9 +145,9 @@ users. Those assets must be built from the tagged source, tested on their target
environment, and kept outside Git and the source archive. A format that was not environment, and kept outside Git and the source archive. A format that was not
built and tested is not presented as supported. built and tested is not presented as supported.
## 5.2.5 release artifacts ## 5.2.6 release artifacts
The 5.2.5 release workflow is defined to produce the following files only after The 5.2.6 release workflow is defined to produce the following files only after
the corresponding target gate succeeds. `SHA256SUMS` records the exact promoted the corresponding target gate succeeds. `SHA256SUMS` records the exact promoted
filenames and digests. The release notes identify the tested commit and the filenames and digests. The release notes identify the tested commit and the
manually dispatched CI run; that run's job definitions and logs are the runtime manually dispatched CI run; that run's job definitions and logs are the runtime
@ -145,23 +156,23 @@ skips. This table is not a substitute for that evidence.
| Format | Intended target and validation boundary | | Format | Intended target and validation boundary |
| --- | --- | | --- | --- |
| `zupt-5.2.5.tar.gz` | Reproducible, source-only archive; scanned twice-built input plus SHA-256. | | `zupt-5.2.6.tar.gz` | Reproducible, source-only archive; scanned twice-built input plus SHA-256. |
| `zupt_5.2.5_amd64.deb` | Ubuntu 24.04 amd64 package; install, functional round trip, and uninstall gate. | | `zupt_5.2.6_amd64.deb` | Ubuntu 24.04 amd64 package; install, functional round trip, and uninstall gate. |
| `zupt-5.2.5-*.x86_64.rpm` and `.src.rpm` | openSUSE Tumbleweed x86_64 source/binary RPM gate; package inspection, install, round trip, and uninstall. | | `zupt-5.2.6-*.x86_64.rpm` and `.src.rpm` | openSUSE Tumbleweed x86_64 source/binary RPM gate; package inspection, install, round trip, and uninstall. |
| `zupt-5.2.5-linux-x86_64.tar.xz` | Linux x86_64 CLI plus the complete public license/notice payload; dependency allowlist and extracted-package functional gate. | | `zupt-5.2.6-linux-x86_64.tar.xz` | Linux x86_64 CLI plus the complete public license/notice payload; dependency allowlist and extracted-package functional gate. |
| `zupt-gui_5.2.5_all.deb` | Architecture-independent Python/Qt GUI package; exact dependency/payload checks plus installed off-screen GUI/CLI integration gate. | | `zupt-gui_5.2.6_all.deb` | Architecture-independent Python/Qt GUI package; exact dependency/payload checks plus installed off-screen GUI/CLI integration gate. |
| `zupt-gui-5.2.5-1.noarch.rpm` | Architecture-independent Python/Qt GUI RPM; package inspection plus installed off-screen GUI/CLI integration gate. | | `zupt-gui-5.2.6-1.noarch.rpm` | Architecture-independent Python/Qt GUI RPM; package inspection plus installed off-screen GUI/CLI integration gate. |
| `zupt-gui-5.2.5-1.src.rpm` | Source RPM corresponding exactly to the gated noarch GUI RPM. | | `zupt-gui-5.2.6-1.src.rpm` | Source RPM corresponding exactly to the gated noarch GUI RPM. |
| `zupt-gui-5.2.5-portable.zip` | Source-only GUI and launchers with licenses/provenance; source scan, exact member allowlist, and extracted off-screen GUI/CLI gate. | | `zupt-gui-5.2.6-portable.zip` | Source-only GUI and launchers with licenses/provenance; source scan, exact member allowlist, and extracted off-screen GUI/CLI gate. |
| `zupt-5.2.5-windows-x86_64.zip` | Native Windows x86_64 executable with notices; extracted-ZIP round-trip gate. | | `zupt-5.2.6-windows-x86_64.zip` | Native Windows x86_64 executable with notices; extracted-ZIP round-trip gate. |
| `ZUPT-5.2.5-macOS-*.dmg` | Native macOS image; mounted packaged executable round-trip gate, with the actual architecture in the filename. | | `ZUPT-5.2.6-macOS-*.dmg` | Native macOS image; mounted packaged executable round-trip gate, with the actual architecture in the filename. |
An asset absent from the release was not promoted through its mandatory gate. An asset absent from the release was not promoted through its mandatory gate.
Do not infer support for another distribution release, OS version, CPU Do not infer support for another distribution release, OS version, CPU
architecture, raw UNC/SMB destination, or package manager from a similarly architecture, raw UNC/SMB destination, or package manager from a similarly
named file. Binary assets are release outputs, never source-build inputs. named file. Binary assets are release outputs, never source-build inputs.
No AppImage is promised for 5.2.5. The inspected upstream type-2 runtime lacked No AppImage is promised for 5.2.6. The inspected upstream type-2 runtime lacked
a complete notice/source-relink handoff for every statically linked component, a complete notice/source-relink handoff for every statically linked component,
so redistributing it would not meet this release's provenance gate. AppDir and so redistributing it would not meet this release's provenance gate. AppDir and
Flatpak bundles and GUI platform installers are likewise outside the promoted Flatpak bundles and GUI platform installers are likewise outside the promoted
@ -191,8 +202,8 @@ bash tests/test_source_only.sh
For a tag or an existing source archive: For a tag or an existing source archive:
~~~sh ~~~sh
bash scripts/check-source-only.sh --tag v5.2.5 bash scripts/check-source-only.sh --tag v5.2.6
bash scripts/check-source-only.sh --archive /path/to/zupt-5.2.5.tar.gz bash scripts/check-source-only.sh --archive /path/to/zupt-5.2.6.tar.gz
~~~ ~~~
Unknown `.bin` files fail the scan. A necessary binary data fixture may be Unknown `.bin` files fail the scan. A necessary binary data fixture may be
@ -328,7 +339,7 @@ sanitizer-detected crash. An earlier off-screen GUI smoke run remains supporting
evidence rather than an exact-candidate package result. evidence rather than an exact-candidate package result.
Those results are historical upstream self-audit evidence, not independent Those results are historical upstream self-audit evidence, not independent
certification and not 5.2.5 results. Post-tag CI integration failures prevented certification and not 5.2.6 results. Post-tag CI integration failures prevented
5.2.2 promotion. The immutable 5.2.3 candidate was also not promoted because its 5.2.2 promotion. The immutable 5.2.3 candidate was also not promoted because its
source-policy test assumed LF for a `.bat` checkout that correctly used CRLF. source-policy test assumed LF for a `.bat` checkout that correctly used CRLF.
The immutable v5.2.4 candidate then recorded 12 successful jobs in exact-tag CI The immutable v5.2.4 candidate then recorded 12 successful jobs in exact-tag CI
@ -336,14 +347,18 @@ run `33431386002`; the sole openSUSE service-harness job failed because the
standalone executor did not enter its service directory, so dependent Windows standalone executor did not enter its service directory, so dependent Windows
and macOS jobs were skipped. A local Tumbleweed reproduction proved the explicit and macOS jobs were skipped. A local Tumbleweed reproduction proved the explicit
tag ref and corrected working-directory contract, but neither that reproduction tag ref and corrected working-directory contract, but neither that reproduction
nor the successful v5.2.4 jobs are v5.2.5 evidence. The exact 5.2.5 candidate nor the successful v5.2.4 jobs are v5.2.6 evidence. The immutable v5.2.5
must repeat all required gates; candidate was not promoted after exact-tag GitHub Actions run `33434986357`:
native Windows and macOS, hosted GitHub CI/release promotion, authenticated OBS, 13 jobs succeeded, but the native Windows hostile-path fixture and macOS
and resolution of the openSUSE automatic `debugsource` rpmlint `no-binary` build/check gate failed. The corrective byte-exact fixture, portable secure-wipe
fallback, and Bash 3.2 array handling therefore require new 5.2.6 evidence. The
exact 5.2.6 candidate must repeat all required gates. Native Windows and macOS,
hosted GitHub CI/release promotion, authenticated OBS, and resolution of the
openSUSE automatic `debugsource` rpmlint `no-binary`
finding remain pending until recorded otherwise. Unexecuted gates are `SKIP`, finding remain pending until recorded otherwise. Unexecuted gates are `SKIP`,
never `PASS`. never `PASS`.
On Windows, 5.2.5 scopes output handling to normal local Win32 paths. A MinGW On Windows, 5.2.6 scopes output handling to normal local Win32 paths. A MinGW
cross-build or Wine run is not native-Windows evidence; the `windows-latest` cross-build or Wine run is not native-Windows evidence; the `windows-latest`
package job, including its Unicode round trip, remains a mandatory publication package job, including its Unicode round trip, remains a mandatory publication
gate. Win32 extended-length and device-namespace paths, raw UNC output roots gate. Win32 extended-length and device-namespace paths, raw UNC output roots
@ -365,7 +380,7 @@ downgrading authentication of header and footer metadata.
`disk restore`, and exists only to recover a known, trusted archive created `disk restore`, and exists only to recover a known, trusted archive created
before AIT was introduced. Do not use that override for an archive from before AIT was introduced. Do not use that override for an archive from
untrusted or attacker-writable storage; verify and migrate the recovered data to untrusted or attacker-writable storage; verify and migrate the recovered data to
a newly created 5.2.5 archive. Compression and disk backup never create a a newly created 5.2.6 archive. Compression and disk backup never create a
no-AIT archive. no-AIT archive.
`info` is deliberately different: it reports unauthenticated framing metadata, `info` is deliberately different: it reports unauthenticated framing metadata,
@ -383,7 +398,7 @@ lists, tests, extracts, and restores it byte-exact. The full local Linux gate
passed on commit `ff99770`. This is not a claim that a 5.2.1 reader understands every new passed on commit `ff99770`. This is not a claim that a 5.2.1 reader understands every new
flag-gated 5.2.2 encoding or that every historical combination was tested. flag-gated 5.2.2 encoding or that every historical combination was tested.
The candidate commands and outcome fields for 5.2.5 are maintained in the The candidate commands and outcome fields for 5.2.6 are maintained in the
release handoff and release handoff and
[packaging/opensuse/README.md](packaging/opensuse/README.md). They must be [packaging/opensuse/README.md](packaging/opensuse/README.md). They must be
updated from the final release candidate before tagging. No architecture or updated from the final release candidate before tagging. No architecture or
@ -395,9 +410,9 @@ Generate the reproducible source archive outside the repository:
~~~sh ~~~sh
make dist make dist
sha256sum /tmp/zupt-5.2.5.tar.gz sha256sum /tmp/zupt-5.2.6.tar.gz
bash scripts/check-source-only.sh \ bash scripts/check-source-only.sh \
--archive /tmp/zupt-5.2.5.tar.gz --archive /tmp/zupt-5.2.6.tar.gz
~~~ ~~~
Archive ordering, ownership and timestamps are normalized. The default epoch is Archive ordering, ownership and timestamps are normalized. The default epoch is
@ -413,7 +428,7 @@ final digest before the tag is published.
## openSUSE and OBS ## openSUSE and OBS
The maintained upstream recipe is in packaging/opensuse. It is prepared for an The maintained upstream recipe is in packaging/opensuse. It is prepared for an
immutable v5.2.5 tag, disables submodules and Git LFS, builds with immutable v5.2.6 tag, disables submodules and Git LFS, builds with
WITH_SDK=0 WITH_PQBOX=0, runs real checks, and installs without the renamed-era WITH_SDK=0 WITH_PQBOX=0, runs real checks, and installs without the renamed-era
`vaptvupt` alias. `vaptvupt` alias.
@ -456,7 +471,7 @@ The optional GUI is under `gui/`. It invokes the `zupt` CLI and needs Python 3
plus PySide6 or PyQt6. GUI image assets are data files whose purpose, plus PySide6 or PyQt6. GUI image assets are data files whose purpose,
provenance and license are recorded in [gui/assets/README.md](gui/assets/README.md). provenance and license are recorded in [gui/assets/README.md](gui/assets/README.md).
The integrated source and lightweight consistency checks do not constitute a The integrated source and lightweight consistency checks do not constitute a
target-native audit of every historical GUI format. The 5.2.5 artifact promise target-native audit of every historical GUI format. The 5.2.6 artifact promise
is limited to the gated GUI DEB, noarch/source RPM, and source-only portable ZIP is limited to the gated GUI DEB, noarch/source RPM, and source-only portable ZIP
listed above; AppImage, AppDir, Flatpak bundles, and platform GUI installers listed above; AppImage, AppDir, Flatpak bundles, and platform GUI installers
remain excluded. remain excluded.
@ -466,13 +481,13 @@ remain excluded.
Cristian Cezar Moisés is the creator and current upstream maintainer of ZUPT and Cristian Cezar Moisés is the creator and current upstream maintainer of ZUPT and
the author of the current upstream source, build, test, documentation, and the author of the current upstream source, build, test, documentation, and
packaging changes, including the 5.2.2 baseline and corrective packaging changes, including the 5.2.2 baseline and corrective
5.2.3/5.2.4/5.2.5 work. 5.2.3/5.2.4/5.2.5/5.2.6 work.
Alessandro de Oliveira Faria (Cabelo) is credited as the openSUSE collaborator Alessandro de Oliveira Faria (Cabelo) is credited as the openSUSE collaborator
and downstream package maintainer. He reviews the handoff, commits it in the and downstream package maintainer. He reviews the handoff, commits it in the
OBS project he maintains, and may make the additional openSUSE-side adjustments OBS project he maintains, and may make the additional openSUSE-side adjustments
he considers necessary. That downstream role is not attribution of ZUPT source he considers necessary. That downstream role is not attribution of ZUPT source
authorship or of the upstream 5.2.2, 5.2.3, 5.2.4, or 5.2.5 changes. authorship or of the upstream 5.2.2, 5.2.3, 5.2.4, 5.2.5, or 5.2.6 changes.
## License ## License

View file

@ -1,4 +1,4 @@
# Security Policy — ZUPT 5.2.5 # Security Policy — ZUPT 5.2.6
## Reporting vulnerabilities ## Reporting vulnerabilities
@ -66,7 +66,7 @@ partially accepted.
### Optional integrations ### Optional integrations
The 5.2.5 default is `WITH_SDK=0 WITH_PQBOX=0`: The 5.2.6 default is `WITH_SDK=0 WITH_PQBOX=0`:
- `WITH_SDK=1` enables libvuptsdk-backed features, including the SDK PQ mode - `WITH_SDK=1` enables libvuptsdk-backed features, including the SDK PQ mode
and Argon2id support, using a separately installed system development package. and Argon2id support, using a separately installed system development package.
@ -133,12 +133,19 @@ can compromise archives encrypted to it.
## Constant-time and side-channel scope ## Constant-time and side-channel scope
Portable C is the 5.2.5 default. Sensitive comparisons and selections use Portable C is the 5.2.6 default. Sensitive comparisons and selections use
branchless helpers, but generated machine-code behavior remains dependent on branchless helpers, but generated machine-code behavior remains dependent on
the compiler and platform. This is not a formal whole-program constant-time the compiler and platform. This is not a formal whole-program constant-time
claim. The C AES implementation uses table lookups and is unsuitable for a claim. The C AES implementation uses table lookups and is unsuitable for a
claim of cache-timing resistance on hostile shared hardware. claim of cache-timing resistance on hostile shared hardware.
Sensitive VaptVupt working buffers are cleared through a compiler-resistant
wipe helper. Platforms with a guaranteed libc `explicit_bzero` use it; macOS
and NetBSD use the portable volatile-write fallback because the supported
deployment targets do not guarantee that symbol. This source-level choice
resists ordinary dead-store elimination but is not a formal claim about every
compiler binary.
Textual assembly under `jasmin/` can be enabled explicitly with Textual assembly under `jasmin/` can be enabled explicitly with
`WITH_JASMIN=1` on a supported x86_64 compiler target. The directory contains `WITH_JASMIN=1` on a supported x86_64 compiler target. The directory contains
Jasmin-generated output and separately identified hand-written assembly; all of Jasmin-generated output and separately identified hand-written assembly; all of
@ -197,7 +204,7 @@ media before proceeding.
The Windows handle-relative implementation is scoped to normal local Win32 The Windows handle-relative implementation is scoped to normal local Win32
paths. Win32 extended-length and device-namespace paths, raw UNC output roots, paths. Win32 extended-length and device-namespace paths, raw UNC output roots,
and mapped/network-drive output are not supported in 5.2.5. Cross-build and and mapped/network-drive output are not supported in 5.2.6. Cross-build and
Wine results are not native-Windows evidence; the `windows-latest` package gate Wine results are not native-Windows evidence; the `windows-latest` package gate
must pass its Unicode round trip before Windows assets are published. Restore must pass its Unicode round trip before Windows assets are published. Restore
to a normal local directory first and move verified output to network storage to a normal local directory first and move verified output to network storage
@ -240,7 +247,7 @@ shared/static library, or distribution package. Audit them with:
```sh ```sh
scripts/check-source-only.sh scripts/check-source-only.sh
scripts/check-source-only.sh --archive /path/to/zupt-5.2.5.tar.gz scripts/check-source-only.sh --archive /path/to/zupt-5.2.6.tar.gz
``` ```
Nested archive inspection is required to enforce bounded recursion, member Nested archive inspection is required to enforce bounded recursion, member
@ -250,13 +257,13 @@ limit violations. On commit `ff99770`, the source-only scanner suite passed
DEB, binary RPM, SRPM, notice-bearing Linux tar.xz, source-only portable GUI DEB, binary RPM, SRPM, notice-bearing Linux tar.xz, source-only portable GUI
ZIP, Windows ZIP, and macOS DMG release assets are separate outputs. An ZIP, Windows ZIP, and macOS DMG release assets are separate outputs. An
AppImage is not promoted for 5.2.5. A bare Linux or Windows executable is also AppImage is not promoted for 5.2.6. A bare Linux or Windows executable is also
excluded; executables are distributed only inside their notice-bearing excluded; executables are distributed only inside their notice-bearing
archives. Trust an artifact only when its exact format has a recorded build, archives. Trust an artifact only when its exact format has a recorded build,
content/metadata inspection, extracted or installed smoke test, and applicable content/metadata inspection, extracted or installed smoke test, and applicable
archive round trip. Never treat an unexecuted platform as passing. archive round trip. Never treat an unexecuted platform as passing.
The gated 5.2.5 set is the CLI package/archive set plus the exact GUI DEB, The gated 5.2.6 set is the CLI package/archive set plus the exact GUI DEB,
noarch/source RPM, and source-only portable ZIP documented in the README. The noarch/source RPM, and source-only portable ZIP documented in the README. The
portable GUI ZIP contains no compiled runtime and is scanned as source before portable GUI ZIP contains no compiled runtime and is scanned as source before
and after extraction. Other GUI packages, AppImage, AppDir and Flatpak bundles, and after extraction. Other GUI packages, AppImage, AppDir and Flatpak bundles,
@ -296,7 +303,7 @@ result.
Post-tag CI integration failures prevented 5.2.2 promotion. Those upstream Post-tag CI integration failures prevented 5.2.2 promotion. Those upstream
self-audit results are not independent certification and do not transfer to self-audit results are not independent certification and do not transfer to
5.2.5. The immutable 5.2.3 candidate was not promoted because its source-policy 5.2.6. The immutable 5.2.3 candidate was not promoted because its source-policy
test assumed LF for a Windows `.bat` file checked out as CRLF. The immutable test assumed LF for a Windows `.bat` file checked out as CRLF. The immutable
v5.2.4 candidate was not promoted after exact-tag GitHub Actions run v5.2.4 candidate was not promoted after exact-tag GitHub Actions run
`33431386002`: 12 jobs succeeded, the sole openSUSE job failed in its `33431386002`: 12 jobs succeeded, the sole openSUSE job failed in its
@ -306,11 +313,18 @@ Tumbleweed reproduction confirmed that `refs/tags/v5.2.4` is valid and that
`os.chdir(service_dir)` lets `obs_scm`, `tar`, and `recompress` complete with a `os.chdir(service_dir)` lets `obs_scm`, `tar`, and `recompress` complete with a
source-scanned archive. This was a release/test integration defect, not a source-scanned archive. This was a release/test integration defect, not a
product, archive, cryptographic, codec, or SDK ABI change, and its evidence does product, archive, cryptographic, codec, or SDK ABI change, and its evidence does
not transfer automatically to 5.2.5. The exact 5.2.5 candidate must repeat the not transfer automatically to 5.2.6. The immutable v5.2.5 candidate was also
required suite. Native Windows and macOS, hosted GitHub CI/release promotion, not promoted: exact-tag GitHub Actions run `33434986357` recorded 13 successful
authenticated OBS, and the openSUSE automatic `debugsource` rpmlint `no-binary` jobs and failed native Windows/macOS jobs. Its Windows fixture-byte and macOS
finding remain pending until recorded otherwise. An unavailable or unexecuted secure-wipe/Bash 3.2 defects are corrected for 5.2.6, but those changes have not
environment remains `SKIP`, never `PASS`. yet passed exact-5.2.6 hosted or native gates. A targeted clean-clone run of the
corrected scanner under genuine GNU Bash 3.2.57 passed repository, standalone
tree, standalone archive, and root-plus-tag modes; that local compatibility
result does not transfer to any other gate. The exact 5.2.6 candidate must
repeat the required suite. Native Windows and macOS, hosted GitHub CI/release
promotion, authenticated OBS, and the openSUSE automatic `debugsource` rpmlint
`no-binary` finding remain pending until recorded otherwise. An unavailable or
unexecuted environment remains `SKIP`, never `PASS`.
Run target-native static analyzers and package checks as additional evidence. Run target-native static analyzers and package checks as additional evidence.
Do not infer x86_64, aarch64, ppc64le, s390x, riscv64, macOS, Windows, Leap, or Do not infer x86_64, aarch64, ppc64le, s390x, riscv64, macOS, Windows, Leap, or

View file

@ -159,7 +159,7 @@ grant attached to their unchanged Git blobs, are recorded in
## AppImage type-2 runtime ## AppImage type-2 runtime
No AppImage is a promised or promoted 5.2.5 release asset. The upstream No AppImage is a promised or promoted 5.2.6 release asset. The upstream
type-2 runtime inspected during the 5.2.2 review statically linked musl, libfuse, type-2 runtime inspected during the 5.2.2 review statically linked musl, libfuse,
squashfuse, zstd, zlib, and mimalloc, but its own license notice did not list squashfuse, zstd, zlib, and mimalloc, but its own license notice did not list
mimalloc and the available release inputs did not provide a complete mimalloc and the available release inputs did not provide a complete
@ -171,7 +171,7 @@ no network input and requires the operator to supply both a locally verified
runtime and `APPIMAGE_RUNTIME_COMPLIANCE_FILE`, containing the license notices, runtime and `APPIMAGE_RUNTIME_COMPLIANCE_FILE`, containing the license notices,
source correspondence or offer, and relink information applicable to those source correspondence or offer, and relink information applicable to those
exact runtime bytes. An artifact produced independently with that helper is exact runtime bytes. An artifact produced independently with that helper is
not covered by the 5.2.5 upstream release gates. not covered by the 5.2.6 upstream release gates.
## Reporting attribution issues ## Reporting attribution issues

View file

@ -1,4 +1,4 @@
# ZUPT 5.2.5 threat model # ZUPT 5.2.6 threat model
This document defines the security boundary of the ZUPT archive tool. It is This document defines the security boundary of the ZUPT archive tool. It is
not a certification, a guarantee against every hostile input, or a substitute not a certification, a guarantee against every hostile input, or a substitute
@ -17,7 +17,7 @@ plausibly deniable.
## Baseline considered here ## Baseline considered here
The upstream baseline is built from the 5.2.5 source with: The upstream baseline is built from the 5.2.6 source with:
```sh ```sh
make WITH_SDK=0 WITH_PQBOX=0 make WITH_SDK=0 WITH_PQBOX=0
@ -175,7 +175,7 @@ temporary through its descriptor or handle. These controls reduce traversal,
link, race, and partial-output risks, but do not establish that no parser or link, race, and partial-output risks, but do not establish that no parser or
filesystem bug can exist. filesystem bug can exist.
The Windows handle-relative boundary in 5.2.5 covers normal local Win32 paths. The Windows handle-relative boundary in 5.2.6 covers normal local Win32 paths.
Win32 extended-length and device-namespace paths, raw UNC output roots, and Win32 extended-length and device-namespace paths, raw UNC output roots, and
mapped/network-drive output are not supported. Cross-build and Wine results are mapped/network-drive output are not supported. Cross-build and Wine results are
not a substitute for the required native `windows-latest` Unicode package not a substitute for the required native `windows-latest` Unicode package
@ -250,9 +250,9 @@ tagged source. Each artifact extends the trust boundary to its builder,
toolchain, runner image, and packaging scripts. Treat it as validated only when toolchain, runner image, and packaging scripts. Treat it as validated only when
the exact target has a recorded build, content/package inspection, extracted or the exact target has a recorded build, content/package inspection, extracted or
installed smoke test, and applicable archive round trip. An AppImage is not installed smoke test, and applicable archive round trip. An AppImage is not
promoted for 5.2.5; bare Linux and Windows executables are also excluded. promoted for 5.2.6; bare Linux and Windows executables are also excluded.
For 5.2.5, that gated artifact scope covers the CLI files plus the exact GUI For 5.2.6, that gated artifact scope covers the CLI files plus the exact GUI
DEB, noarch/source RPM, and source-only portable ZIP named in the README. The DEB, noarch/source RPM, and source-only portable ZIP named in the README. The
portable ZIP contains no compiled runtime and crosses the release boundary only portable ZIP contains no compiled runtime and crosses the release boundary only
after source scans and an exact safe-member check. AppDir and Flatpak bundles after source scans and an exact safe-member check. AppDir and Flatpak bundles
@ -265,7 +265,7 @@ strict Clang, GCC `-fanalyzer`, the 9/9 tool-enabled static-analysis run,
ASan/UBSan/LSan, and 1,000 mutation-fuzz iterations passed. Earlier off-screen ASan/UBSan/LSan, and 1,000 mutation-fuzz iterations passed. Earlier off-screen
GUI smoke evidence is retained separately. Post-tag CI integration failures GUI smoke evidence is retained separately. Post-tag CI integration failures
prevented 5.2.2 promotion. This upstream self-review is not an independent prevented 5.2.2 promotion. This upstream self-review is not an independent
certification and is not 5.2.5 evidence. The immutable 5.2.3 candidate was not certification and is not 5.2.6 evidence. The immutable 5.2.3 candidate was not
promoted because its source-policy test assumed LF for a Windows `.bat` checkout promoted because its source-policy test assumed LF for a Windows `.bat` checkout
that correctly used CRLF. The immutable v5.2.4 candidate was not promoted after that correctly used CRLF. The immutable v5.2.4 candidate was not promoted after
exact-tag GitHub Actions run `33431386002`: 12 jobs succeeded, the sole openSUSE exact-tag GitHub Actions run `33431386002`: 12 jobs succeeded, the sole openSUSE
@ -275,9 +275,13 @@ Tumbleweed reproduction established that the explicit `refs/tags/v5.2.4`
revision works and that `os.chdir(service_dir)` completes the source-service revision works and that `os.chdir(service_dir)` completes the source-service
chain. This narrows the failure to release/test integration; it changes no chain. This narrows the failure to release/test integration; it changes no
product, archive, cryptographic, codec, or SDK ABI boundary and supplies no product, archive, cryptographic, codec, or SDK ABI boundary and supplies no
automatic 5.2.5 evidence. Hosted GitHub CI and release promotion, native automatic 5.2.6 evidence. The immutable v5.2.5 candidate was not promoted after
exact-tag GitHub Actions run `33434986357`: 13 jobs succeeded, but native
Windows and macOS failed on fixture-byte preservation and Darwin/Bash 3.2
portability respectively. The corresponding 5.2.6 corrections do not establish
their own test result. Hosted GitHub CI and release promotion, native
Windows/macOS, authenticated OBS, and the openSUSE automatic `debugsource` Windows/macOS, authenticated OBS, and the openSUSE automatic `debugsource`
rpmlint `no-binary` finding remain pending until an exact 5.2.5 candidate rpmlint `no-binary` finding remain pending until an exact 5.2.6 candidate
records them. records them.
## Historical compatibility notes ## Historical compatibility notes
@ -305,7 +309,7 @@ These are historical facts about earlier releases, retained to support recovery:
combinations remain unclaimed. combinations remain unclaimed.
Historical test counts in the changelog describe those releases. They do not Historical test counts in the changelog describe those releases. They do not
automatically become 5.2.5 results; current outcomes belong in the release automatically become 5.2.6 results; current outcomes belong in the release
validation record, with unavailable environments marked `SKIP`. In particular, validation record, with unavailable environments marked `SKIP`. In particular,
runs made before the final positional-AAD and mandatory-AIT changes are not runs made before the final positional-AAD and mandatory-AIT changes are not
final release gates for the resulting candidate. final release gates for the resulting candidate.
@ -316,4 +320,4 @@ Email **zupt@riseup.net** with `[security]` in the subject. Include the version,
platform, impact, and a minimal non-sensitive reproducer. Do not disclose the platform, impact, and a minimal non-sensitive reproducer. Do not disclose the
issue publicly until a coordinated timeline has been agreed. issue publicly until a coordinated timeline has been agreed.
Document version: 5.2.5, 2026-08-31. Document version: 5.2.6, 2026-08-31.

View file

@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: AGPL-3.0-or-later .\" SPDX-License-Identifier: AGPL-3.0-or-later
.\" Copyright (c) 2025-2026 Cristian Cezar Moisés .\" Copyright (c) 2025-2026 Cristian Cezar Moisés
.TH ZUPT-GUI 1 "2026-08-31" "ZUPT 5.2.5" "User Commands" .TH ZUPT-GUI 1 "2026-08-31" "ZUPT 5.2.6" "User Commands"
.SH NAME .SH NAME
zupt-gui \- Qt interface for the ZUPT backup utility zupt-gui \- Qt interface for the ZUPT backup utility
.SH SYNOPSIS .SH SYNOPSIS
@ -47,7 +47,7 @@ or
only when libvuptsdk or libpqvaptvupt is independently reported enabled. only when libvuptsdk or libpqvaptvupt is independently reported enabled.
These two optional integrations are detected separately. These two optional integrations are detected separately.
.PP .PP
The gated 5.2.5 GUI release set is limited to the architecture-independent DEB, The gated 5.2.6 GUI release set is limited to the architecture-independent DEB,
noarch/source RPM, and source-only portable ZIP named in the project README. noarch/source RPM, and source-only portable ZIP named in the project README.
Package gates require exact checks and installed off-screen GUI/CLI integration. Package gates require exact checks and installed off-screen GUI/CLI integration.
The portable ZIP receives source scans, an exact safe-member allowlist, and an The portable ZIP receives source scans, an exact safe-member allowlist, and an

View file

@ -1,6 +1,6 @@
.\" SPDX-License-Identifier: AGPL-3.0-or-later .\" SPDX-License-Identifier: AGPL-3.0-or-later
.\" Copyright (c) 2025-2026 Cristian Cezar Moisés .\" Copyright (c) 2025-2026 Cristian Cezar Moisés
.TH ZUPT 1 "2026-08-31" "ZUPT 5.2.5" "User Commands" .TH ZUPT 1 "2026-08-31" "ZUPT 5.2.6" "User Commands"
. .
.SH NAME .SH NAME
zupt \- source-built backup compression and authenticated-encryption utility zupt \- source-built backup compression and authenticated-encryption utility
@ -89,7 +89,7 @@ Git and the upstream source tarball are source-only. Separately built CLI DEB,
binary RPM, SRPM, notice-bearing Linux tar.xz, Windows ZIP, and macOS DMG assets binary RPM, SRPM, notice-bearing Linux tar.xz, Windows ZIP, and macOS DMG assets
may be published from the immutable tag only after their target-specific gates may be published from the immutable tag only after their target-specific gates
pass; they never enter Git or the source tarball. An AppImage is not promoted pass; they never enter Git or the source tarball. An AppImage is not promoted
for 5.2.5; neither are AppDir/Flatpak bundles, GUI platform installers, or bare for 5.2.6; neither are AppDir/Flatpak bundles, GUI platform installers, or bare
Linux/Windows executables. The Python/Qt frontend remains available as source; Linux/Windows executables. The Python/Qt frontend remains available as source;
its gated architecture-independent DEB, noarch/source RPM, and source-only its gated architecture-independent DEB, noarch/source RPM, and source-only
portable ZIP are included in the release claim. The portable ZIP contains no portable ZIP are included in the release claim. The portable ZIP contains no
@ -477,7 +477,7 @@ then traverse below a pinned directory descriptor with no-follow operations.
Windows builds use handle-relative traversal and Windows builds use handle-relative traversal and
no-replace publication for normal local Win32 destinations. Extended-length and no-replace publication for normal local Win32 destinations. Extended-length and
device-namespace paths, raw UNC output roots, and mapped/network-drive output device-namespace paths, raw UNC output roots, and mapped/network-drive output
are not supported in 5.2.5. Cross-compilation and Wine results are not native are not supported in 5.2.6. Cross-compilation and Wine results are not native
Windows evidence; the native Windows package gate, including its Unicode round Windows evidence; the native Windows package gate, including its Unicode round
trip, is separate and mandatory before publication. trip, is separate and mandatory before publication.
. .
@ -577,7 +577,7 @@ and the encrypted-dedup linear AAD sequence published through 5.2.1. The narrow
compatibility fixture is an actual v5.2.1 password-encrypted compatibility fixture is an actual v5.2.1 password-encrypted
DATA/DATA/REF/DATA disk archive stored as hexadecimal text with source and hash DATA/DATA/REF/DATA disk archive stored as hexadecimal text with source and hash
provenance. The candidate lists, tests, extracts, and restores that fixture provenance. The candidate lists, tests, extracts, and restores that fixture
byte-exact. The exact 5.2.5 candidate must repeat the gate. It does not imply byte-exact. The exact 5.2.6 candidate must repeat the gate. It does not imply
that a 5.2.1 reader that a 5.2.1 reader
accepts every new 5.2.2 archive or that every historical encrypted mode was accepts every new 5.2.2 archive or that every historical encrypted mode was
retested. retested.

View file

@ -1,6 +1,6 @@
# ZUPT GUI # ZUPT GUI
The ZUPT GUI is a Python/Qt front end for the ZUPT 5.2.5 command-line The ZUPT GUI is a Python/Qt front end for the ZUPT 5.2.6 command-line
program. It starts the CLI as a subprocess; compression, archive parsing, and program. It starts the CLI as a subprocess; compression, archive parsing, and
cryptography remain in the C program. cryptography remain in the C program.
@ -83,10 +83,10 @@ operating systems and must be tested on the target system.
Release pages provide only these GUI artifacts after their separate package and Release pages provide only these GUI artifacts after their separate package and
installed off-screen GUI/CLI integration gates pass: installed off-screen GUI/CLI integration gates pass:
- `zupt-gui_5.2.5_all.deb`; - `zupt-gui_5.2.6_all.deb`;
- `zupt-gui-5.2.5-1.noarch.rpm`; - `zupt-gui-5.2.6-1.noarch.rpm`;
- `zupt-gui-5.2.5-1.src.rpm`; - `zupt-gui-5.2.6-1.src.rpm`;
- `zupt-gui-5.2.5-portable.zip`. - `zupt-gui-5.2.6-portable.zip`.
The DEB/RPM packages install the Python/Qt source and depend on the matching The DEB/RPM packages install the Python/Qt source and depend on the matching
`zupt` CLI package. The portable ZIP contains source, launchers, icons, licenses, `zupt` CLI package. The portable ZIP contains source, launchers, icons, licenses,
@ -97,7 +97,7 @@ An absent artifact did not pass its gate and must not be inferred from another
format's result. format's result.
GUI AppImage, AppDir and Flatpak bundles, and Windows/macOS GUI installers are GUI AppImage, AppDir and Flatpak bundles, and Windows/macOS GUI installers are
not promoted by the upstream 5.2.5 release gates. not promoted by the upstream 5.2.6 release gates.
`packaging/build-gui-appimage.sh` is a downstream-only helper and fails unless `packaging/build-gui-appimage.sh` is a downstream-only helper and fails unless
its operator supplies the exact verified runtime plus a complete its operator supplies the exact verified runtime plus a complete
license/source-relink notice through `APPIMAGE_RUNTIME_COMPLIANCE_FILE`; that license/source-relink notice through `APPIMAGE_RUNTIME_COMPLIANCE_FILE`; that
@ -110,7 +110,7 @@ notices. It fails unless the directory also has non-empty
`PYTHON-NOTICE.txt`, `PYINSTALLER-NOTICE.txt`, `QT-NOTICE.txt`, and either `PYTHON-NOTICE.txt`, `PYINSTALLER-NOTICE.txt`, `QT-NOTICE.txt`, and either
`PYSIDE6-NOTICE.txt` or `PYQT6-NOTICE.txt`. The installer includes that `PYSIDE6-NOTICE.txt` or `PYQT6-NOTICE.txt`. The installer includes that
directory together with every ZUPT license and notice. This requirement does directory together with every ZUPT license and notice. This requirement does
not make the untested GUI installer a 5.2.5 release asset. The promoted Windows not make the untested GUI installer a 5.2.6 release asset. The promoted Windows
ZIP and macOS DMG are CLI-only. ZIP and macOS DMG are CLI-only.
Packaging recipes and scripts under `gui/packaging/` and `packaging/` are build Packaging recipes and scripts under `gui/packaging/` and `packaging/` are build

View file

@ -1,9 +1,9 @@
Package: zupt-gui Package: zupt-gui
Version: 5.2.5 Version: 5.2.6
Section: utils Section: utils
Priority: optional Priority: optional
Architecture: all Architecture: all
Depends: python3 (>= 3.9), python3-pyqt6 | python3-pyside6.qtwidgets, zupt (= 5.2.5) Depends: python3 (>= 3.9), python3-pyqt6 | python3-pyside6.qtwidgets, zupt (= 5.2.6)
Maintainer: Cristian Cezar Moisés <sac@securityops.co> Maintainer: Cristian Cezar Moisés <sac@securityops.co>
Homepage: https://github.com/cristiancmoises/zupt Homepage: https://github.com/cristiancmoises/zupt
Description: Qt graphical interface for the ZUPT backup utility Description: Qt graphical interface for the ZUPT backup utility

View file

@ -13,7 +13,7 @@ rem runtime files embedded by this local build.
setlocal EnableExtensions setlocal EnableExtensions
for %%I in ("%~dp0\..\..\..") do set "REPO_ROOT=%%~fI" for %%I in ("%~dp0\..\..\..") do set "REPO_ROOT=%%~fI"
set "VERSION=%~1" set "VERSION=%~1"
if not defined VERSION set "VERSION=5.2.5" if not defined VERSION set "VERSION=5.2.6"
if not defined ZUPT_DIST_DIR set "ZUPT_DIST_DIR=%TEMP%\zupt-release" if not defined ZUPT_DIST_DIR set "ZUPT_DIST_DIR=%TEMP%\zupt-release"
if not defined ZUPT_CLI_EXE set "ZUPT_CLI_EXE=%REPO_ROOT%\zupt.exe" if not defined ZUPT_CLI_EXE set "ZUPT_CLI_EXE=%REPO_ROOT%\zupt.exe"
set "WORK=%TEMP%\zupt-gui-build-%RANDOM%-%RANDOM%" set "WORK=%TEMP%\zupt-gui-build-%RANDOM%-%RANDOM%"

View file

@ -120,7 +120,8 @@ static inline int zupt_win_mkdir_utf8(const char *path) {
/* v5.2.3 corrects release packaging and CI; archive format remains v1.6. */ /* v5.2.3 corrects release packaging and CI; archive format remains v1.6. */
/* v5.2.4 makes package metadata checks CRLF-safe; format remains v1.6. */ /* v5.2.4 makes package metadata checks CRLF-safe; format remains v1.6. */
/* v5.2.5 corrects the OBS service harness cwd; format remains v1.6. */ /* v5.2.5 corrects the OBS service harness cwd; format remains v1.6. */
#define ZUPT_VERSION_STRING "5.2.5" /* v5.2.6 corrects native release-gate portability; format remains v1.6. */
#define ZUPT_VERSION_STRING "5.2.6"
/* Vendored codec release (upstream tag) — single source for display strings. /* Vendored codec release (upstream tag) — single source for display strings.
* The codec's own VV_VERSION_* is its internal API version, not the release. */ * The codec's own VV_VERSION_* is its internal API version, not the release. */
#define ZUPT_CODEC_RELEASE "2.65.3" #define ZUPT_CODEC_RELEASE "2.65.3"

View file

@ -6,7 +6,7 @@
set -Eeuo pipefail set -Eeuo pipefail
umask 077 umask 077
VERSION=${VERSION:-5.2.5} VERSION=${VERSION:-5.2.6}
PREFIX=${PREFIX:-/usr/local} PREFIX=${PREFIX:-/usr/local}
echo "🔧 Installing ZUPT..." echo "🔧 Installing ZUPT..."

View file

@ -11,7 +11,7 @@
# Test locally with `makepkg -s` after the release archive is published. # Test locally with `makepkg -s` after the release archive is published.
pkgname=zupt pkgname=zupt
pkgver=5.2.5 pkgver=5.2.6
pkgrel=1 pkgrel=1
pkgdesc='Pure-C11 post-quantum backup compression utility (AES-256-CTR + HMAC-SHA256 + ML-KEM-768 + X25519)' pkgdesc='Pure-C11 post-quantum backup compression utility (AES-256-CTR + HMAC-SHA256 + ML-KEM-768 + X25519)'
arch=('x86_64') arch=('x86_64')
@ -22,8 +22,8 @@ makedepends=('gcc' 'git' 'make')
checkdepends=('python') checkdepends=('python')
source=("${pkgname}-${pkgver}.tar.gz::https://github.com/cristiancmoises/zupt/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz") source=("${pkgname}-${pkgver}.tar.gz::https://github.com/cristiancmoises/zupt/releases/download/v${pkgver}/${pkgname}-${pkgver}.tar.gz")
# Byte-reproducible upstream v5.2.5 source archive. # Byte-reproducible upstream v5.2.6 source archive. Pin after final generation.
sha256sums=('7b86d34a418a2bce24396610bd7c23705f58d27873e610728083bd57ebb69ba4') sha256sums=('REPLACE_AFTER_FINAL_ARCHIVE_SHA256')
build() { build() {
cd "${pkgname}-${pkgver}" cd "${pkgname}-${pkgver}"

View file

@ -1,3 +1,13 @@
zupt (5.2.6-1) UNRELEASED; urgency=medium
* Use the compiler-resistant volatile wipe fallback on macOS and NetBSD, and
make the source scanner's empty-array handling compatible with Bash 3.2.
* Preserve hostile archive-path fixture bytes exactly on Windows.
* Preserve the immutable, unpromoted 5.2.5 history and require fresh 5.2.6
package, checksum, native-platform, OBS, and promotion gates.
-- Cristian Cezar Moisés <sac@securityops.co> Mon, 31 Aug 2026 21:30:00 +0000
zupt (5.2.5-1) UNRELEASED; urgency=medium zupt (5.2.5-1) UNRELEASED; urgency=medium
* Run the standalone OBS source-service chain from its isolated working * Run the standalone OBS source-service chain from its isolated working

View file

@ -62,7 +62,7 @@
xcb-util-renderutil xcb-util-wm xcb-util-cursor xcb-util-renderutil xcb-util-wm xcb-util-cursor
libinput-minimal mtdev libevdev eudev)) libinput-minimal mtdev libevdev eudev))
(define %zupt-version "5.2.5") (define %zupt-version "5.2.6")
(define %zupt-source (define %zupt-source
(origin (origin
@ -72,7 +72,7 @@
"/releases/download/v" %zupt-version "/releases/download/v" %zupt-version
"/zupt-" %zupt-version ".tar.gz")) "/zupt-" %zupt-version ".tar.gz"))
(sha256 (sha256
(base32 "194vnvmmggc3h1r11rkkg395hpvh4dybs43674jcwawa855d71kv")))) (base32 "REPLACE_AFTER_FINAL_ARCHIVE_GUIX_BASE32"))))
(define-public zupt (define-public zupt
(package (package

View file

@ -22,9 +22,9 @@
class Zupt < Formula class Zupt < Formula
desc "Post-quantum backup compression utility (ML-KEM-768 + AES-256-CTR + HMAC-SHA256)" desc "Post-quantum backup compression utility (ML-KEM-768 + AES-256-CTR + HMAC-SHA256)"
homepage "https://github.com/cristiancmoises/zupt" homepage "https://github.com/cristiancmoises/zupt"
url "https://github.com/cristiancmoises/zupt/releases/download/v5.2.5/zupt-5.2.5.tar.gz" url "https://github.com/cristiancmoises/zupt/releases/download/v5.2.6/zupt-5.2.6.tar.gz"
version "5.2.5" version "5.2.6"
sha256 "7b86d34a418a2bce24396610bd7c23705f58d27873e610728083bd57ebb69ba4" sha256 "REPLACE_AFTER_FINAL_ARCHIVE_SHA256"
license all_of: ["AGPL-3.0-or-later", "GPL-3.0-or-later", "BSD-2-Clause", "BSD-3-Clause", "CC0-1.0"] license all_of: ["AGPL-3.0-or-later", "GPL-3.0-or-later", "BSD-2-Clause", "BSD-3-Clause", "CC0-1.0"]
depends_on "python@3.12" => :test # only for test-suite tamper harness depends_on "python@3.12" => :test # only for test-suite tamper harness

View file

@ -9,7 +9,7 @@
# nix flake check # lint the flake # nix flake check # lint the flake
# #
# To consume from another flake: # To consume from another flake:
# inputs.zupt.url = "github:cristiancmoises/zupt/v5.2.5"; # inputs.zupt.url = "github:cristiancmoises/zupt/v5.2.6";
# ...packages.x86_64-linux.default = inputs.zupt.packages.x86_64-linux.zupt; # ...packages.x86_64-linux.default = inputs.zupt.packages.x86_64-linux.zupt;
# #
# `make dist` has its own reproducibility gate. This development flake has no # `make dist` has its own reproducibility gate. This development flake has no
@ -30,7 +30,7 @@
zupt = pkgs.stdenv.mkDerivation { zupt = pkgs.stdenv.mkDerivation {
pname = "zupt"; pname = "zupt";
version = "5.2.5"; version = "5.2.6";
# When publishing, replace this with `fetchurl` against the # When publishing, replace this with `fetchurl` against the
# release tarball. For local development the flake assumes it # release tarball. For local development the flake assumes it

View file

@ -1,4 +1,4 @@
# ZUPT 5.2.5 for openSUSE Build Service # ZUPT 5.2.6 for openSUSE Build Service
This directory is the upstream, source-only OBS recipe for ZUPT. It is a This directory is the upstream, source-only OBS recipe for ZUPT. It is a
handoff for the downstream maintainer; its presence does not mean that the handoff for the downstream maintainer; its presence does not mean that the
@ -10,14 +10,14 @@ changes in this handoff. Alessandro de Oliveira Faria (Cabelo) is credited only
as the openSUSE collaborator and downstream OBS package maintainer: he reviews as the openSUSE collaborator and downstream OBS package maintainer: he reviews
the handoff, commits it through the portal/project he maintains, and may make the handoff, commits it through the portal/project he maintains, and may make
the openSUSE-side adjustments he considers necessary. This role does not the openSUSE-side adjustments he considers necessary. This role does not
attribute upstream code or the 5.2.2/5.2.3/5.2.4/5.2.5 upstream changes to attribute upstream code or the 5.2.2/5.2.3/5.2.4/5.2.5/5.2.6 upstream changes to
Cabelo. Cabelo.
## Files and source policy ## Files and source policy
| File | Purpose | | File | Purpose |
|---|---| |---|---|
| `_service` | Fetch the immutable `v5.2.5` tag and create `Source0` at build time. | | `_service` | Fetch the immutable `v5.2.6` tag and create `Source0` at build time. |
| `zupt.spec` | Build and test the CLI with optional external system integrations disabled. | | `zupt.spec` | Build and test the CLI with optional external system integrations disabled. |
| `zupt.changes` | openSUSE-format package history. | | `zupt.changes` | openSUSE-format package history. |
| `source-audit.sh` | Handoff wrapper for the repository scanner; run it from the complete handoff tree. | | `source-audit.sh` | Handoff wrapper for the repository scanner; run it from the complete handoff tree. |
@ -30,11 +30,11 @@ https://github.com/cristiancmoises/zupt.git
``` ```
`obs_scm` stores an `.obscpio` plus `.obsinfo`. The `tar` and `recompress` `obs_scm` stores an `.obscpio` plus `.obsinfo`. The `tar` and `recompress`
services reconstruct `zupt-5.2.5.tar.gz` inside the build environment, which services reconstruct `zupt-5.2.6.tar.gz` inside the build environment, which
matches `Source0` in the spec. matches `Source0` in the spec.
This source policy does not prohibit separately built release-page packages. This source policy does not prohibit separately built release-page packages.
The upstream 5.2.5 gates may publish the CLI source tarball, DEB, binary RPM, The upstream 5.2.6 gates may publish the CLI source tarball, DEB, binary RPM,
SRPM, notice-bearing Linux tar.xz, Windows ZIP, and macOS DMG, together with a SRPM, notice-bearing Linux tar.xz, Windows ZIP, and macOS DMG, together with a
GUI DEB, noarch RPM, GUI SRPM, and source-only portable GUI ZIP after each GUI DEB, noarch RPM, GUI SRPM, and source-only portable GUI ZIP after each
format-specific test succeeds. None of those files is an OBS `Source0` input format-specific test succeeds. None of those files is an OBS `Source0` input
@ -139,7 +139,7 @@ reconstructed by the build-time services. Neither `%build` nor `%check` may
access the network. access the network.
For a source RPM check outside OBS, place the service-produced For a source RPM check outside OBS, place the service-produced
`zupt-5.2.5.tar.gz` next to the spec and use a disposable RPM build tree: `zupt-5.2.6.tar.gz` next to the spec and use a disposable RPM build tree:
```sh ```sh
rpm_top=$(mktemp -d) rpm_top=$(mktemp -d)
@ -167,7 +167,7 @@ unavailable rather than passing it. Earlier off-screen GUI smoke evidence is
supporting evidence, not an exact-commit package result. supporting evidence, not an exact-commit package result.
Post-tag CI integration failures prevented 5.2.2 promotion. These historical Post-tag CI integration failures prevented 5.2.2 promotion. These historical
local results do not establish 5.2.5, native Windows or macOS success, hosted local results do not establish 5.2.6, native Windows or macOS success, hosted
GitHub CI/release promotion, authenticated OBS acceptance, or resolution of the GitHub CI/release promotion, authenticated OBS acceptance, or resolution of the
automatic openSUSE `debugsource` rpmlint `no-binary` finding. The immutable automatic openSUSE `debugsource` rpmlint `no-binary` finding. The immutable
5.2.3 candidate was not promoted because its source-policy test assumed LF for 5.2.3 candidate was not promoted because its source-policy test assumed LF for
@ -194,10 +194,21 @@ This result establishes that the explicit tag revision works and isolates a
release/test harness defect. It does not change the product, archive format, release/test harness defect. It does not change the product, archive format,
cryptography, codec, or SDK ABI; it does not make skipped native jobs pass or cryptography, codec, or SDK ABI; it does not make skipped native jobs pass or
establish authenticated OBS/Factory acceptance. No v5.2.4 evidence transfers establish authenticated OBS/Factory acceptance. No v5.2.4 evidence transfers
automatically to v5.2.5. The exact v5.2.5 candidate must repeat every applicable automatically to v5.2.6. The exact v5.2.6 candidate must repeat every applicable
gate, and the automatic openSUSE `debugsource` rpmlint `no-binary` finding gate, and the automatic openSUSE `debugsource` rpmlint `no-binary` finding
remains unresolved and unsuppressed. remains unresolved and unsuppressed.
## Prior 5.2.5 exact-tag native-gate evidence
The immutable v5.2.5 candidate was not promoted. Exact-tag GitHub Actions run
`33434986357` completed 13 jobs successfully and failed the native Windows and
macOS jobs. Windows exposed a hostile-path fixture that did not preserve its
requested bytes across the command-line boundary; macOS exposed the unsupported
`explicit_bzero` assumption and Bash 3.2 empty-array handling. The 5.2.6
corrections address those release/test integration defects without an archive,
cryptographic, codec, or SDK ABI change. They do not establish 5.2.6 hosted,
native, OBS, or promotion evidence.
## Prior openSUSE packaging validation ## Prior openSUSE packaging validation
The local results below were produced on 2026-08-24 from the 5.2.2 candidate The local results below were produced on 2026-08-24 from the 5.2.2 candidate
@ -235,11 +246,11 @@ gate.
## Handoff procedure for Alessandro/Cabelo ## Handoff procedure for Alessandro/Cabelo
1. Upstream completes every applicable pre-tag source and local audit gate, 1. Upstream completes every applicable pre-tag source and local audit gate,
then creates and verifies the annotated `v5.2.5` tag. Exact-tag hosted, then creates and verifies the annotated `v5.2.6` tag. Exact-tag hosted,
native-platform, package, and promotion gates must pass before release or native-platform, package, and promotion gates must pass before release or
downstream handoff; the tag itself is never moved to repair a failure. downstream handoff; the tag itself is never moved to repair a failure.
2. With Git, `file`, bsdtar, tar, zip, unzip and SHA-256 tools installed, run 2. With Git, `file`, bsdtar, tar, zip, unzip and SHA-256 tools installed, run
`scripts/export-opensuse-package.sh v5.2.5`. Verify the reported ZIP and `scripts/export-opensuse-package.sh v5.2.6`. Verify the reported ZIP and
SHA-256 outside the Git index. The handoff includes both SHA-256 outside the Git index. The handoff includes both
`packaging/opensuse/source-audit.sh` and its required `packaging/opensuse/source-audit.sh` and its required
`scripts/check-source-only.sh`; keep that relative layout while auditing. `scripts/check-source-only.sh`; keep that relative layout while auditing.
@ -251,7 +262,7 @@ gate.
``` ```
4. From the extracted handoff root, run 4. From the extracted handoff root, run
`packaging/opensuse/source-audit.sh --archive /path/to/zupt-5.2.5.tar.gz`. `packaging/opensuse/source-audit.sh --archive /path/to/zupt-5.2.6.tar.gz`.
Then copy `_service`, `zupt.spec`, `zupt.changes` and `README.md` Then copy `_service`, `zupt.spec`, `zupt.changes` and `README.md`
into the flat OBS package checkout. The audit wrapper is not an OBS build into the flat OBS package checkout. The audit wrapper is not an OBS build
source and must not be copied without its companion `scripts/` directory. source and must not be copied without its companion `scripts/` directory.

View file

@ -4,7 +4,7 @@
<service name="obs_scm" mode="manual"> <service name="obs_scm" mode="manual">
<param name="url">https://github.com/cristiancmoises/zupt.git</param> <param name="url">https://github.com/cristiancmoises/zupt.git</param>
<param name="scm">git</param> <param name="scm">git</param>
<param name="revision">refs/tags/v5.2.5</param> <param name="revision">refs/tags/v5.2.6</param>
<param name="versionformat">@PARENT_TAG@</param> <param name="versionformat">@PARENT_TAG@</param>
<param name="versionrewrite-pattern">^v(.*)$</param> <param name="versionrewrite-pattern">^v(.*)$</param>
<param name="versionrewrite-replacement">\1</param> <param name="versionrewrite-replacement">\1</param>

View file

@ -1,3 +1,13 @@
-------------------------------------------------------------------
Mon Aug 31 21:30:00 UTC 2026 - Cristian Cezar Moisés <sac@securityops.co>
- Update to 5.2.6:
* Use the compiler-resistant volatile wipe fallback on macOS and NetBSD.
* Make source-scanner empty-array handling compatible with Bash 3.2.
* Preserve hostile archive-path fixture bytes exactly on Windows.
* Preserve immutable, unpromoted 5.2.5 history and require fresh 5.2.6 gates.
* Pin the OBS source service to the immutable v5.2.6 tag.
------------------------------------------------------------------- -------------------------------------------------------------------
Mon Aug 31 19:55:00 UTC 2026 - Cristian Cezar Moisés <sac@securityops.co> Mon Aug 31 19:55:00 UTC 2026 - Cristian Cezar Moisés <sac@securityops.co>

View file

@ -18,7 +18,7 @@
# #
Name: zupt Name: zupt
Version: 5.2.5 Version: 5.2.6
Release: 0 Release: 0
Summary: Backup compression with authenticated and post-quantum encryption Summary: Backup compression with authenticated and post-quantum encryption
License: AGPL-3.0-or-later AND GPL-3.0-or-later AND BSD-2-Clause AND BSD-3-Clause AND CC0-1.0 License: AGPL-3.0-or-later AND GPL-3.0-or-later AND BSD-2-Clause AND BSD-3-Clause AND CC0-1.0

View file

@ -27,7 +27,7 @@ Requirements
------------ ------------
1. Python 3.9 or newer. 1. Python 3.9 or newer.
2. PySide6 6.5 or newer, or a compatible PyQt6 package. 2. PySide6 6.5 or newer, or a compatible PyQt6 package.
3. ZUPT 5.2.5, installed as `zupt` on PATH or placed beside the launcher 3. ZUPT 5.2.6, installed as `zupt` on PATH or placed beside the launcher
(`zupt.exe` on Windows). A local command must have been built (`zupt.exe` on Windows). A local command must have been built
and tested independently; this bundle never downloads one. and tested independently; this bundle never downloads one.
@ -46,7 +46,7 @@ Troubleshooting
--------------- ---------------
* "requires PySide6 or PyQt6": install one Qt binding through your operating * "requires PySide6 or PyQt6": install one Qt binding through your operating
system package manager or another trusted, preconfigured Python source. system package manager or another trusted, preconfigured Python source.
* "zupt not found": install ZUPT 5.2.5 or place its command beside * "zupt not found": install ZUPT 5.2.6 or place its command beside
the launcher. the launcher.
* Set ZUPT_DEBUG=1 to print command-discovery diagnostics to stderr. * Set ZUPT_DEBUG=1 to print command-discovery diagnostics to stderr.

View file

@ -20,7 +20,7 @@
# installed smoke test. # installed smoke test.
Name: zupt Name: zupt
Version: 5.2.5 Version: 5.2.6
Release: 1%{?dist} Release: 1%{?dist}
Summary: Backup compression with authenticated and post-quantum encryption Summary: Backup compression with authenticated and post-quantum encryption
@ -101,6 +101,12 @@ comments. Plain archives use non-cryptographic checksums.
%endif %endif
%changelog %changelog
* Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.2.6-1
- Correct native release gates: use the secure volatile wipe fallback on
macOS and NetBSD, support Bash 3.2 empty arrays in the source scanner, and
preserve hostile archive-path fixture bytes exactly on Windows.
- Preserve immutable, unpromoted 5.2.5 history and require fresh 5.2.6 gates.
* Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.2.5-1 * Mon Aug 31 2026 Cristian Cezar Moisés <sac@securityops.co> - 5.2.5-1
- Run the standalone OBS source-service chain from its isolated working - Run the standalone OBS source-service chain from its isolated working
directory and add a packaging-policy regression for that contract. directory and add a packaging-policy regression for that contract.

View file

@ -13,6 +13,9 @@ HAVE_EXTERNAL_TARGET=0
declare -a TAGS=() declare -a TAGS=()
declare -a ARCHIVES=() declare -a ARCHIVES=()
declare -a TREES=() declare -a TREES=()
TAG_COUNT=0
ARCHIVE_COUNT=0
TREE_COUNT=0
FAILURES=0 FAILURES=0
SCANNED=0 SCANNED=0
@ -92,17 +95,20 @@ while (($#)); do
--tag) --tag)
(($# >= 2)) || { printf 'ERROR: --tag requires a revision\n' >&2; exit 2; } (($# >= 2)) || { printf 'ERROR: --tag requires a revision\n' >&2; exit 2; }
TAGS+=("$2") TAGS+=("$2")
TAG_COUNT=$((TAG_COUNT + 1))
shift 2 shift 2
;; ;;
--archive) --archive)
(($# >= 2)) || { printf 'ERROR: --archive requires a file\n' >&2; exit 2; } (($# >= 2)) || { printf 'ERROR: --archive requires a file\n' >&2; exit 2; }
ARCHIVES+=("$2") ARCHIVES+=("$2")
ARCHIVE_COUNT=$((ARCHIVE_COUNT + 1))
HAVE_EXTERNAL_TARGET=1 HAVE_EXTERNAL_TARGET=1
shift 2 shift 2
;; ;;
--tree) --tree)
(($# >= 2)) || { printf 'ERROR: --tree requires a directory\n' >&2; exit 2; } (($# >= 2)) || { printf 'ERROR: --tree requires a directory\n' >&2; exit 2; }
TREES+=("$2") TREES+=("$2")
TREE_COUNT=$((TREE_COUNT + 1))
HAVE_EXTERNAL_TARGET=1 HAVE_EXTERNAL_TARGET=1
shift 2 shift 2
;; ;;
@ -127,7 +133,7 @@ while (($#)); do
esac esac
done done
if ((HAVE_EXTERNAL_TARGET)) && ((ROOT_REQUESTED == 0)) && ((${#TAGS[@]} == 0)); then if ((HAVE_EXTERNAL_TARGET)) && ((ROOT_REQUESTED == 0)) && ((TAG_COUNT == 0)); then
REPOSITORY_AUDIT=0 REPOSITORY_AUDIT=0
fi fi
@ -268,7 +274,9 @@ path_stays_below_root() {
[[ $candidate != /* && $candidate != //* ]] || return 1 [[ $candidate != /* && $candidate != //* ]] || return 1
[[ ! $candidate =~ ^[[:alpha:]]: ]] || return 1 [[ ! $candidate =~ ^[[:alpha:]]: ]] || return 1
read -r -a components <<< "$candidate" read -r -a components <<< "$candidate"
for component in "${components[@]}"; do # Bash 3.2 treats an empty array expansion as unset under `set -u`.
# The + guard expands to no words for an empty path component list.
for component in ${components[@]+"${components[@]}"}; do
case $component in case $component in
''|.) ;; ''|.) ;;
..) ..)
@ -821,38 +829,44 @@ if ((REPOSITORY_AUDIT)); then
else else
fail_path git-archive-HEAD HEAD 'repository has no commit' fail_path git-archive-HEAD HEAD 'repository has no commit'
fi fi
for target in "${TAGS[@]}"; do if ((TAG_COUNT)); then
if git -C "$ROOT" rev-parse --verify -q "$target^{commit}" >/dev/null; then for target in "${TAGS[@]}"; do
scan_git_archive "$ROOT" "$target" "git-archive-$target" if git -C "$ROOT" rev-parse --verify -q "$target^{commit}" >/dev/null; then
scan_git_archive "$ROOT" "$target" "git-archive-$target"
else
fail_path git-tag "$target" 'revision does not resolve to a commit'
fi
done
fi
fi
if ((TREE_COUNT)); then
for target in "${TREES[@]}"; do
if [[ -d $target ]]; then
target=$(canonicalize_allow_missing "$target") || {
fail_path standalone-tree "$target" 'cannot canonicalize tree'
continue
}
scan_tree "$target" standalone-tree 0
else else
fail_path git-tag "$target" 'revision does not resolve to a commit' fail_path standalone-tree "$target" 'tree does not exist'
fi fi
done done
fi fi
for target in "${TREES[@]}"; do if ((ARCHIVE_COUNT)); then
if [[ -d $target ]]; then for target in "${ARCHIVES[@]}"; do
target=$(canonicalize_allow_missing "$target") || { if [[ -f $target ]]; then
fail_path standalone-tree "$target" 'cannot canonicalize tree' target=$(canonicalize_allow_missing "$target") || {
continue fail_path standalone-archive "$target" 'cannot canonicalize archive'
} continue
scan_tree "$target" standalone-tree 0 }
else scan_archive "$target" "${target##*/}" standalone-archive 0
fail_path standalone-tree "$target" 'tree does not exist' else
fi fail_path standalone-archive "$target" 'archive does not exist'
done fi
done
for target in "${ARCHIVES[@]}"; do fi
if [[ -f $target ]]; then
target=$(canonicalize_allow_missing "$target") || {
fail_path standalone-archive "$target" 'cannot canonicalize archive'
continue
}
scan_archive "$target" "${target##*/}" standalone-archive 0
else
fail_path standalone-archive "$target" 'archive does not exist'
fi
done
if ((FAILURES == 0)); then if ((FAILURES == 0)); then
printf 'PASS source-only: %d files, %d archives\n' "$SCANNED" "$ARCHIVES_SCANNED" printf 'PASS source-only: %d files, %d archives\n' "$SCANNED" "$ARCHIVES_SCANNED"

View file

@ -41,14 +41,15 @@
* *
* Implementation strategy: * Implementation strategy:
* - Prefer `explicit_bzero` (BSD/glibc 2.25+, guaranteed-secure) * - Prefer `explicit_bzero` (BSD/glibc 2.25+, guaranteed-secure)
* - Fall back to `memset_explicit` (C23) * - Otherwise use a volatile-pointer loop (compiler cannot
* - Last resort: volatile-pointer memset (compiler cannot
* prove the writes are dead) * prove the writes are dead)
* */ * */
#if defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25)) #if defined(__GLIBC__) && (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 25))
# define VV_HAS_EXPLICIT_BZERO 1 # define VV_HAS_EXPLICIT_BZERO 1
#elif defined(__APPLE__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) /* Darwin intentionally uses the volatile fallback: current deployment targets
* do not guarantee an explicit_bzero symbol in libSystem. */
#elif defined(__FreeBSD__) || defined(__OpenBSD__)
# define VV_HAS_EXPLICIT_BZERO 1 # define VV_HAS_EXPLICIT_BZERO 1
#else #else
# define VV_HAS_EXPLICIT_BZERO 0 # define VV_HAS_EXPLICIT_BZERO 0

View file

@ -42,6 +42,31 @@ static size_t put_varint(uint8_t *out, uint64_t value) {
return count; return count;
} }
static int hex_nibble(unsigned char value) {
if (value >= '0' && value <= '9') return (int)(value - '0');
if (value >= 'a' && value <= 'f') return (int)(value - 'a') + 10;
if (value >= 'A' && value <= 'F') return (int)(value - 'A') + 10;
return -1;
}
static int decode_hex_entry(const char *hex, uint8_t *out, size_t capacity,
size_t *out_size) {
size_t hex_size = strlen(hex);
if (hex_size == 0 || (hex_size & 1u) != 0 ||
hex_size / 2u >= capacity)
return -1;
size_t decoded_size = hex_size / 2u;
for (size_t i = 0; i < decoded_size; i++) {
int high = hex_nibble((unsigned char)hex[i * 2u]);
int low = hex_nibble((unsigned char)hex[i * 2u + 1u]);
if (high < 0 || low < 0) return -1;
out[i] = (uint8_t)((high << 4) | low);
}
*out_size = decoded_size;
return 0;
}
static int write_block(FILE *stream, uint8_t type, const uint8_t *payload, static int write_block(FILE *stream, uint8_t type, const uint8_t *payload,
size_t payload_size, uint64_t unpacked_size, size_t payload_size, uint64_t unpacked_size,
uint64_t checksum) { uint64_t checksum) {
@ -66,11 +91,24 @@ static int write_block(FILE *stream, uint8_t type, const uint8_t *payload,
int main(int argc, char **argv) { int main(int argc, char **argv) {
static const uint8_t content[] = "fixture content\n"; static const uint8_t content[] = "fixture content\n";
const char *entry = argc == 3 && strncmp(argv[2], "--entry=", 8) == 0 uint8_t decoded_entry[ZUPT_MAX_PATH];
? argv[2] + 8 : NULL; const uint8_t *entry = NULL;
if (!entry || argv[1][0] == '\0' || entry[0] == '\0' || size_t path_size = 0;
strlen(entry) >= ZUPT_MAX_PATH) {
fprintf(stderr, "usage: %s ARCHIVE --entry=ENTRY_PATH\n", argv[0]); if (argc == 3 && strncmp(argv[2], "--entry=", 8) == 0) {
entry = (const uint8_t *)argv[2] + 8;
path_size = strlen(argv[2] + 8);
} else if (argc == 3 &&
strncmp(argv[2], "--entry-hex=", 12) == 0 &&
decode_hex_entry(argv[2] + 12, decoded_entry,
sizeof(decoded_entry), &path_size) == 0) {
entry = decoded_entry;
}
if (!entry || argv[1][0] == '\0' || path_size == 0 ||
path_size >= ZUPT_MAX_PATH) {
fprintf(stderr,
"usage: %s ARCHIVE --entry=ENTRY_PATH|--entry-hex=HEX_BYTES\n",
argv[0]);
return 2; return 2;
} }
@ -106,7 +144,6 @@ int main(int argc, char **argv) {
uint8_t index[ZUPT_MAX_PATH + 128]; uint8_t index[ZUPT_MAX_PATH + 128];
size_t index_size = 0; size_t index_size = 0;
size_t path_size = strlen(entry);
index_size += put_varint(index + index_size, 1); index_size += put_varint(index + index_size, 1);
index_size += put_varint(index + index_size, path_size); index_size += put_varint(index + index_size, path_size);
memcpy(index + index_size, entry, path_size); memcpy(index + index_size, entry, path_size);

View file

@ -104,33 +104,62 @@ else
fail 'control-byte archive path is rejected without terminal injection' fail 'control-byte archive path is rejected without terminal injection'
fi fi
expect_display_unsafe_path_rejected() { file_contains_hex_bytes() {
local label=$1 name=$2 entry=$3 python3 - "$1" "$2" <<'PY'
import pathlib
import sys
data = pathlib.Path(sys.argv[1]).read_bytes()
needle = bytes.fromhex(sys.argv[2])
raise SystemExit(0 if needle in data else 1)
PY
}
expect_display_unsafe_hex_path_rejected() {
local label=$1 name=$2 entry_hex=$3 forbidden_hex=$4
local archive=$TEST_ROOT/$name.zupt log=$TEST_ROOT/$name.log status local archive=$TEST_ROOT/$name.zupt log=$TEST_ROOT/$name.log status
MSYS2_ARG_CONV_EXCL='--entry=' "$FIXTURE" "$archive" "--entry=$entry" MSYS2_ARG_CONV_EXCL='--entry-hex=' \
"$FIXTURE" "$archive" "--entry-hex=$entry_hex"
if ! file_contains_hex_bytes "$archive" "$entry_hex"; then
printf ' fixture did not preserve the requested path bytes: %s\n' \
"$entry_hex" >&2
fail "$label"
return
fi
set +e set +e
"$ZUPT_BIN" list "$archive" > "$log" 2>&1 "$ZUPT_BIN" list "$archive" > "$log" 2>&1
status=$? status=$?
set -e set -e
if ((status != 0)) && ! LC_ALL=C grep -Fq -- "$entry" "$log"; then if ((status != 0)) && ! file_contains_hex_bytes "$log" "$forbidden_hex"; then
pass "$label" pass "$label"
else else
fail "$label" fail "$label"
fi fi
} }
expect_display_unsafe_path_rejected \ if MSYS2_ARG_CONV_EXCL='--entry-hex=' \
"$FIXTURE" "$TEST_ROOT/invalid-hex.zupt" '--entry-hex=0' \
>/dev/null 2>&1 ||
MSYS2_ARG_CONV_EXCL='--entry-hex=' \
"$FIXTURE" "$TEST_ROOT/invalid-hex.zupt" '--entry-hex=GG' \
>/dev/null 2>&1; then
fail 'archive path fixture rejects malformed hex input'
else
pass 'archive path fixture rejects malformed hex input'
fi
expect_display_unsafe_hex_path_rejected \
'raw C1 archive path is rejected without terminal injection' \ 'raw C1 archive path is rejected without terminal injection' \
raw-c1 $'safe\23331m.txt' raw-c1 736166659b33316d2e747874 9b
expect_display_unsafe_path_rejected \ expect_display_unsafe_hex_path_rejected \
'UTF-8 C1 archive path is rejected without terminal injection' \ 'UTF-8 C1 archive path is rejected without terminal injection' \
utf8-c1 $'safe\302\23331m.txt' utf8-c1 73616665c29b33316d2e747874 c29b
expect_display_unsafe_path_rejected \ expect_display_unsafe_hex_path_rejected \
'Unicode bidi-control archive path is rejected without display spoofing' \ 'Unicode bidi-control archive path is rejected without display spoofing' \
bidi $'safe\342\200\256exe.txt' bidi 73616665e280ae6578652e747874 e280ae
expect_display_unsafe_path_rejected \ expect_display_unsafe_hex_path_rejected \
'invalid UTF-8 archive path is rejected without raw display' \ 'invalid UTF-8 archive path is rejected without raw display' \
invalid-utf8 $'safe\300\257.txt' invalid-utf8 73616665c0af2e747874 c0af
make_fixture "$TEST_ROOT/leaf.zupt" 'innocent.txt' make_fixture "$TEST_ROOT/leaf.zupt" 'innocent.txt'
printf '%s\n' DO_NOT_OVERWRITE > "$TEST_ROOT/sentinel" printf '%s\n' DO_NOT_OVERWRITE > "$TEST_ROOT/sentinel"