Skip to content

fix(appimage): bump the Tauri CLI and guard AppDir permissions - #373

Merged
drmowinckels merged 6 commits into
mainfrom
fix/appimage-apprun-permissions
Oct 6, 2026
Merged

drmowinckels merged 6 commits into
mainfrom
fix/appimage-apprun-permissions

Conversation

@drmowinckels

Copy link
Copy Markdown
Owner

Summary

The AppImage catalog retest on 0.0.13 (appimage.github.io#9123) got past .DirIcon — that fix did land — and failed on something worse:

/run/firejail/appimage/AppRun: line 12: /run/firejail/appimage/AppRun.wrapped: Permission denied
ERROR: The application exited within 11 seconds instead of showing a window

AppRun.wrapped is packaged -rwxrwx---. This is tauri-apps/tauri#16155: write_and_make_executable sets 0o770 on every tool the bundler downloads, and fs::copy carries that mode into <AppDir>/AppRun, which linuxdeploy-plugin-gtk renames to AppRun.wrapped.

Scope of the bug, stated precisely: launched the normal way this is invisible, because the runtime's FUSE mount reports the files as owned by whoever started the image, so the owner bits apply. It only bites when root mounts the image and a different user runs it — firejail --appimage, which is exactly how the catalog tests submissions, and some sandboxed launchers. So this is not "the AppImage is broken for everyone"; ordinary double-click and shell launches were fine.

What changed

  • @tauri-apps/cli 2.11.5 → 2.12.1. Upstream closed #16155 as completed, and the maintainer's comment says it is "fixed in 2.12 already".
  • The preview job's existing Verify AppImage AppDir files step now also asserts AppDir permissions.

The tauri crate deliberately stays on 2.11.1 — the bug is in the bundler, which ships with the CLI, and moving the runtime risks the cold-overlay regressions from #196/#226 for no benefit here.

Why the version bump alone isn't the whole change

I could not verify upstream's claim, and the source contradicts it. On dev HEAD, crates/tauri-bundler/src/bundle/linux/appimage/mod.rs still reads:

fs::set_permissions(path, fs::Permissions::from_mode(0o770))?;

and linuxdeploy.rs still does a plain fs::copy of AppRun-<arch> into the AppDir, which preserves the mode. The bundler CHANGELOG has no entry for it either (newest is 2.10.1). So the bump may well not fix it.

Rather than cut a release to find out, the preview check now asserts the invariant directly, as a general property instead of against AppRun by name:

  • every AppDir entry is world-readable;
  • every owner-executable file is world-executable.

That is what chmod -R a+rX AppDir would guarantee, and it is what the catalog actually requires. If 2.12.1 does not fix it, this check fails and names the offending paths instead of us shipping a third rejected AppImage.

Verification

I ran audit:workflow-shell (82 steps parse) and actionlint — both clean — plus format:check and audit:spell. The permission assertion itself needs GNU find -printf, so it cannot run on macOS; it is exercised by adding the build:installers label to this PR, which builds a real AppImage on ubuntu-22.04. I am doing that now and will report what the check says.

No tests: this is a toolchain version bump plus a CI assertion, with no application code to exercise.

Next step after this merges

The catalog tests the latest release, so clearing #9123 needs a release built with whatever actually fixes the mode, then /retest on that PR.

🤖 Generated with Claude Code

The AppImage catalog rejected 0.0.13 a second time. The `.DirIcon` fix
landed, but the next failure is that `AppRun.wrapped` is packaged
`-rwxrwx---`, so the app cannot start at all when root mounts the image
and another user runs it:

    /run/firejail/appimage/AppRun: AppRun.wrapped: Permission denied

That is tauri-apps/tauri#16155: `write_and_make_executable` sets 0o770
on the tools the bundler downloads, and `fs::copy` carries the mode into
<AppDir>/AppRun, which linuxdeploy-plugin-gtk renames to AppRun.wrapped.
It is invisible in normal use because the runtime's FUSE mount reports
the files as owned by whoever launched the image.

Bumps @tauri-apps/cli 2.11.5 -> 2.12.1, which upstream says carries the
fix. Treating that as unverified: the bundler source on `dev` still sets
0o770, so the preview job now asserts the invariant directly — every
AppDir entry world-readable, every owner-executable file
world-executable — rather than trusting the version bump. If 2.12.1 does
not fix it, that check fails and says so instead of shipping another
rejected AppImage.

Only the CLI moves; the `tauri` crate stays on 2.11.1, since the bug is
in the bundler and bumping the runtime risks the overlay regressions
from #196/#226.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@drmowinckels drmowinckels added the build:installers Build preview .deb/.rpm/.msi/.dmg installers as PR artifacts label Oct 5, 2026
@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

🔍 Advisory audit report

These checks don't block merges — they surface drift in dependencies, licensing, and external links.

cargo-deny

⚠️ findings
�[0m�[1m�[38;5;9merror[vulnerability]�[0m�[1m: Wasmtime component async-lifted callback result count is unvalidated, causing a native stack buffer overflow�[0m
    �[0m�[36m┌─�[0m /home/runner/work/entracte/entracte/src-tauri/Cargo.lock:560:1
    �[0m�[36m│�[0m
�[0m�[36m560�[0m �[0m�[36m│�[0m �[0m�[31mwasmtime 43.0.2 registry+https://github.com/rust-lang/crates.io-index�[0m
    �[0m�[36m│�[0m �[0m�[31m━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━�[0m �[0m�[31msecurity vulnerability detected�[0m
    �[0m�[36m│�[0m
    �[0m�[36m├�[0m ID: RUSTSEC-2026-0327
    �[0m�[36m├�[0m Advisory: https://rustsec.org/advisories/RUSTSEC-2026-0327
    �[0m�[36m├�[0m This is an entry in the RustSec database for the Wasmtime security advisory
      located at
      https://github.com/bytecodealliance/wasmtime/security/advisories/GHSA-32h6-97mm-8q3c
      For more information see the GitHub-hosted security advisory.
    �[0m�[36m├�[0m Announcement: https://github.com/bytecodealliance/wasmtime/pull/14471
    �[0m�[36m├�[0m Solution: Upgrade to >=48.0.4, <49.0.0 OR >=49.0.2 (try `cargo update -p wasmtime`)
    �[0m�[36m├�[0m wasmtime v43.0.2
      ├── extism v1.30.0
      │   └── entracte v0.0.13
      ├── wasi-common v43.0.2
      │   └── extism v1.30.0 (*)
      └── wiggle v43.0.2
          ├── extism v1.30.0 (*)
          └── wasi-common v43.0.2 (*)

advisories �[31mFAILED�[0m, bans �[32mok�[0m, licenses �[32mok�[0m, sources �[32mok�[0m

lychee (broken links)

✅ all links resolve

npm audit

⚠️ findings
# npm audit report

braces  *
Severity: high
braces vulnerable to stack-exhaustion denial of service through deeply nested patterns - https://github.com/advisories/GHSA-vfj7-8cjw-p6xm
fix available via `npm audit fix --force`
Will install stylelint@7.7.0, which is a breaking change
node_modules/braces
  micromatch  >=0.2.0
  Depends on vulnerable versions of braces
  node_modules/micromatch
    fast-glob  *
    Depends on vulnerable versions of micromatch
    node_modules/fast-glob
      globby  >=8.0.0
      Depends on vulnerable versions of fast-glob
      node_modules/globby
        stylelint  >=7.7.1
        Depends on vulnerable versions of fast-glob
        Depends on vulnerable versions of globby
        Depends on vulnerable versions of micromatch
        node_modules/stylelint
          stylelint-config-recommended  *
          Depends on vulnerable versions of stylelint
          node_modules/stylelint-config-recommended
            stylelint-config-standard  >=16.0.0
            Depends on vulnerable versions of stylelint
            Depends on vulnerable versions of stylelint-config-recommended
            node_modules/stylelint-config-standard

smol-toml  <=1.8.0
Severity: moderate
smol-toml: Quadratic-time parse() from parseKey rescanning to end of document on each key line - https://github.com/advisories/GHSA-r4xh-jqrq-34v2
fix available via `npm audit fix`
node_modules/smol-toml

source-map-js  1.0.0 - 1.2.1
Severity: high
source-map-js allows event-loop denial of service through indexed source-map section offsets - https://github.com/advisories/GHSA-68fv-2mgg-jv7q
fix available via `npm audit fix`
node_modules/source-map-js

9 vulnerabilities (1 moderate, 8 high)

To address issues that do not require attention, run:
  npm audit fix

To address all issues (including breaking changes), run:
  npm audit fix --force

@github-actions

github-actions Bot commented Oct 5, 2026 •

Copy link
Copy Markdown
Contributor

📖 Docs preview

✅ Built and deployed for commit 359c7968ec74489f593dbf79cf281baee2a37d18.

Preview URL https://pr-373--entract.netlify.app
Build logs https://github.com/drmowinckels/entracte/actions/runs/37449921835

Posted by docs-preview.yml — updates in place on every push.

drmowinckels and others added 2 commits October 5, 2026 17:35
The AppImage stage stalled with no output for over two hours on three
separate runs today, after building fine on 2026-10-02. It is not the CLI
bump in this branch: a control run on 2.11.5 with only the permissions
guard hung identically.

A probe of the runner image found the cause. `ubuntu22
/ 20260927.309.1` ships fuse3 and libfuse3 but no libfuse2, and
linuxdeploy and its plugins are themselves AppImages that self-mount via
FUSE 2:

    $ ./linuxdeploy-x86_64.AppImage --version
    dlopen(): error loading libfuse.so.2
    $ APPIMAGE_EXTRACT_AND_RUN=1 ./linuxdeploy-x86_64.AppImage --version
    linuxdeploy version 1-alpha ...

Neither workflow installed libfuse2 — both relied on the image carrying
it, which it did until this image refresh. Declaring the dependency is
the fix that survives the next refresh; APPIMAGE_EXTRACT_AND_RUN=1 would
also work but changes how every bundled tool runs, to route around a
package we can simply install.

release.yml gets the same line: it bundles the AppImage identically, so a
release tagged today would have hung in build-unix rather than failing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 89.72%. Comparing base (236f4e7) to head (359c796).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #373      +/-   ##
==========================================
- Coverage   89.72%   89.72%   -0.01%     
==========================================
  Files         148      148              
  Lines       25671    25671              
  Branches      861      861              
==========================================
- Hits        23034    23033       -1     
- Misses       2611     2612       +1     
  Partials       26       26              
Flag Coverage Δ
frontend 90.39% <ø> (ø)
rust 89.64% <ø> (-0.01%) ⬇️
Components Coverage Δ
Frontend (TypeScript) 90.39% <ø> (ø)
Backend (Rust) 89.64% <ø> (-0.01%) ⬇️
see 1 file with indirect coverage changes
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

drmowinckels and others added 3 commits October 6, 2026 12:15
…ters

Two defects in the check this branch added.

It took three hours to answer. The Linux preview built every bundle, and
bundling the rpm from an unstripped DEBUG binary takes 2h44m — measured
in run 36894026697, `Bundling ...rpm` at 17:12:39 and the next line at
19:56:42. I read that as a hang and cancelled four runs that were
progressing normally. The Linux preview now builds deb + appimage only;
rpm is still exercised by release.yml, where the release profile does it
in ~27s.

It asserted on the wrong thing. `--appimage-extract` does not faithfully
reproduce stored directory modes, so the blanket "everything must be
world-readable" check reported 143 `drwx------` directories — which the
AppImage catalog traverses without complaint, so it is a false positive,
and it masked the real result. Modes are now read from the squashfs
metadata via `unsquashfs -lls`, and the assertion names the launchers:
AppRun and AppRun.wrapped must be world-readable and world-executable,
which is exactly the failure that rejected 0.0.13. Anything else that is
not world-readable is reported as a warning rather than failing, since
there is no evidence it breaks a launch.

The run that finally completed (37334101263) answered the original
question: `.DirIcon` and the `.desktop` entry both resolve, and no file
is owner-executable-only — so @tauri-apps/cli 2.12.1 does fix
tauri-apps/tauri#16155, despite `from_mode(0o770)` still being present in
the bundler source on `dev`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
$OLDPWD is only set after a cd, so referencing it outside the extract
subshell tripped `set -u`:

    line 57: OLDPWD: unbound variable

The symlink checks had already passed by then, so the failure was mine,
not the bundle's. Resolve the path once, up front.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@drmowinckels drmowinckels removed the build:installers Build preview .deb/.rpm/.msi/.dmg installers as PR artifacts label Oct 6, 2026
@drmowinckels
drmowinckels merged commit c7b9b8f into main Oct 6, 2026
18 checks passed
@drmowinckels
drmowinckels deleted the fix/appimage-apprun-permissions branch October 6, 2026 10:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant