Report security issues privately via GitHub's Report a vulnerability advisory flow (Security tab) or by email to the maintainer. Please do not open public issues for undisclosed vulnerabilities. Include reproduction steps and affected version/platform.
Zinnia is a single-user desktop application. It runs the bundled 7-Zip binary as a sidecar; it does not run a server or accept network input beyond the signed update check.
All 7z invocations go through validate_run_7z_args
(src-tauri/src/validation.rs), the security
boundary between frontend-supplied arguments and the spawned process:
- Only the commands
a,u,x,l,t,bare permitted. - A mandatory
--separator divides switches from paths; switches are allow-listed by prefix. - Null bytes and over-length arguments are rejected.
- Positional paths and the
-ooutput directory may not contain a..parent-directory segment (defense-in-depth against path traversal, independent of frontend validation). - Destructive
-sdeloperations are rejected. Users delete source files explicitly after verifying the archive.
Arguments are passed to the sidecar as an array, never via a shell string, so command injection is not possible.
Zinnia does not let 7-Zip write directly into the final output for mutating operations:
- Create/update writes to a sibling staging basename. Only a successful process promotes the complete output family, including split volumes.
- Extraction writes to a contained staging directory. Before extraction, Zinnia
copies the complete input volume family into a private, recovery-tracked
snapshot. Member listing and extraction both use that same snapshot, so an
ordinary source-file replacement cannot change what is extracted after
preflight. Zinnia lists members (
7z l -slt) and rejects paths with..or absolute forms that could escape the-oroot into an existing sibling folder. Before promotion, Zinnia also snapshots sibling names in the stage parent (new names outside the stage fail closed), walks the staged tree, rejects absolute or escaping symbolic links and Windows reparse points (relative in-tree links used by macOS.app/.frameworkbundles are allowed), rejects unsupported file types, and applies entry-count and expanded-size ceilings. - Create/update passes
-snl/-snhso symbolic and hard links inside selected folders (for example macOS app bundles) are stored as links rather than followed. The backend also injects these switches on create/update so they cannot be omitted by the webview. The selected input path itself must still be a real file or directory (symlink/reparse inputs are rejected), except for relative symlink members under a managed convert temp directory (so Convert can round-trip top-level links). Nested Windows junctions / cloud placeholders inside a compress tree are rejected (fail closed); ZIP still cannot faithfully round-trip many symlink trees, so the UI warns when ZIP is chosen for inputs that contain symlinks or.appbundles—prefer7zortar. -sns(NTFS alternate streams) and-sni(NT security descriptors) remain blocked: packing ADS / ACLs is a known hiding and privilege footgun.- On macOS, after a successful extract promote, Zinnia clears
com.apple.quarantineon.appbundles under the destination (so Gatekeeper does not treat a user-initiated app extract like an untrusted download) and reports how many bundles were cleared. Quarantine is not stripped from the whole tree—broad clearing is a known Gatekeeper-bypass pattern. Clearing quarantine does not make untrusted software safe to run. - On Windows extract, Zinnia injects 7-Zip
-snzso Mark-of-the-Web (Zone.Identifier) propagates from a downloaded archive onto extracted files (SmartScreen / Office Protected View). Zinnia does not strip MOTW. - On Unix, after extract, Zinnia may restore the execute bit on files that look like binaries or scripts (ELF / Mach-O / shebang / common extensions) when the archive format omitted Unix modes (common with ZIP).
- Promotion resolves file/directory conflicts without overwriting unrelated destination content. A durable move plan and transaction journal allow an interrupted merge or split-archive promotion to be rolled back on restart.
- Cancel/close keeps the global operation slot locked until the child has exited and staging cleanup has completed.
- Extraction growth is limited by both expansion ratio and current free disk space, with capacity reserved for the OS and other applications.
These checks are defense in depth around 7-Zip's own path sanitization. They do not make untrusted archives harmless: users should still keep Zinnia and its bundled 7-Zip current and avoid opening extracted executables they do not trust.
Zinnia removes -pPASSWORD before spawning 7-Zip and supplies the password to
7-Zip's prompt through a short-lived stdin pipe. Create/update receives a bare
-p switch so 7-Zip prompts instead of silently creating an unencrypted
archive; list/test/extract prompt automatically. The pipe is bounded and then
closed so an unexpected prompt cannot leave the sidecar waiting indefinitely.
The password is therefore not present in the spawned process's command line or
ordinary process listings.
Passwords are never written to the activity log, command preview, or persisted
settings (redacted via sanitizeCommandArgsForPreview /
redactSensitiveText). Passwords containing line breaks are rejected because
the prompt transport is line-oriented.
The password necessarily exists transiently in Zinnia and 7-Zip process memory and in the OS pipe buffer. A process with sufficient same-user debugging or memory-inspection access, a crash dump, or a compromised user session may still recover it. This transport protects against incidental command-line disclosure; it does not protect secrets from an attacker who can inspect or control the running user account.
The 7-Zip binaries in assets/ are committed to the repository and checksummed
in assets/7z-checksums.json. Exact official source URLs, downloaded archive
hashes, versions, and extracted members are recorded in
assets/7z-provenance.json. Because they are bundled, a 7-Zip CVE fix
requires manually updating the binaries, regenerating checksums, and shipping a
new Zinnia release; there is no OS-level automatic update mechanism.
Action: watch the 7-Zip release page
and NVD vendor page
for new advisories. When a new 7-Zip version addresses a security issue, update
assets/ with the new binaries, run
assets/7z-provenance.json with the exact official archive URLs, archive
SHA-256 values, and extracted member mapping, then run
node scripts/prepare-7z.js --update-checksums --all --version <verified-version> --verify-downloads <download-directory> --trusted-7z <independently-trusted-7z-path>
and cut a Zinnia release. The update command refuses to run when its explicit
version does not match the reviewed provenance manifest or the downloaded
archives and extracted members do not match that manifest. The trusted
extractor must be an independently installed file outside this repository's
candidate assets/ and generated src-tauri/binaries/ roots; it is used only
to unpack the official Windows .7z, while official .tar.xz sources use the
system tar.
The published data for CVE-2026-58052 is currently inconsistent: the NVD/CNA
affected range was revised to end at 26.01, while the NVD analysis and upstream
7-Zip ticket still describe 26.02 as affected. Until the exact bundled Windows
runtime is conclusively verified against the published reproducer, Zinnia
conservatively rejects RAR extraction on Windows at the run_7z spawn
boundary (command x) when the attested probe_7z version is 26.02 or
older (or unknown). RAR browse (l) and test (t) remain available so
archives can be inspected without writing members to disk. Base
tauri.conf.json omits RAR file associations; macOS/Linux platform configs
re-add them. Windows packages continue to omit RAR associations and Explorer
verbs. RAR browsing, testing, conversion, and extraction remain available on
macOS and Linux.
When a fixed 7-Zip ships and probe_7z attests a version newer than 26.02,
the Windows RAR extract gate lifts automatically. Keep the bundled sidecar and
checksums updated in the same release.
Basic mode may enable OS-native window glass (macOSPrivateApi +
window-vibrancy). This is cosmetic only: Power mode and Linux stay opaque,
and effects-off paints a solid background via set_background_color. The
webview still runs under the same CSP and command allow-lists; translucency
does not expand filesystem or network reach.
The Flatpak package grants --filesystem=home, --filesystem=xdg-download,
--filesystem=/run/media, --filesystem=/media, and --filesystem=/mnt because the bundled 7-Zip
sidecar must read/write user-selected archive paths, including common USB and
download locations outside $HOME. Document portals alone cannot cover sidecar
I/O today. This expands the sandbox blast radius relative to a portal-only app;
treat untrusted archives with the same caution as on other platforms.
There is intentionally no --share=network. Flatpak builds do not use the
in-app GitHub updater (Settings update UI is hidden); refresh via Flathub or a
reinstalled sideload bundle instead.
src-tauri/.cargo/audit.toml contains a deliberately narrow, documented list
of transitive Tauri/wry/GTK3 advisories. CI fails for every advisory outside
that list. Before each stable release, review the ignored list against the
resolved dependency tree and remove an ignore as soon as Tauri provides an
upgrade path. In particular, the GTK glib VariantStrIter soundness advisory
is not reached by Zinnia's code, but it remains a Linux runtime dependency and
must not be treated as resolved merely because cargo audit allows it.
Zinnia re-checks extraction ancestors immediately before publishing staged output. A same-user process can still race the final rename or hard_link after that check. Fully eliminating that residual race requires platform-specific no-follow directory handles; it is tracked as architectural security debt. The current staging, canonical-path, symlink/reparse, and post-extraction validation checks remain mandatory defense in depth.
On Unix, promote opens use O_NOFOLLOW for the final path component. On
Windows, open_regular_file_nofollow opens with FILE_FLAG_OPEN_REPARSE_POINT
and rejects reparse tags on the opened handle. Archive publish prefers
hard_link while that handle is held and falls back to copying from the same
handle (no path re-open). Residual same-user TOCTOU remains for the hard_link
path name lookup itself.
The main window may only open_path directories that a recent successful
compress/extract promoted. Extract-only windows bind their destination folder at
window spawn (derived from the archive path). They may only extract to that
folder (-o) and may only open_path that same folder after registering it.
This is defense in depth against a compromised webview writing or opening
arbitrary folders.