fix(updater): close TOCTOU and downgrade in the root Linux update helper - #581
Merged
Merged
Conversation
The passwordless self-update helper runs as root via pkexec but takes a caller-supplied package path that is writable by the unprivileged invoking user. It verified the detached signature and then re-opened the same path for `dpkg -i` / `rpm -U` — two separate opens, so the bytes could be swapped between verify and install, giving a local user root code execution. The helper now copies the package and its signature into a root-owned 0700 working dir first, then verifies and installs the copies, so the verified bytes cannot be replaced. The deb path additionally refuses anything that is not a strict version increase (`dpkg --compare-versions ... gt`), blocking a rollback to an older, still-validly-signed, vulnerable release; `rpm -U` already refuses downgrades. The bash is exercised on real distros in packaging; a new unit test locks the security-relevant structure (copy-before-verify, verify-the-copy, refuse non-upgrade) so a refactor cannot silently drop it. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Security fix — F-1 / F-2 (audit 2026-08-19)
Severity: High (local privilege escalation) · Module:
plugin-build/plugin(LinuxUpdateHelper)This is the only code path in the project that reaches root.
Problem
The passwordless self-update helper is installed as a package-owned file and invoked as root through a polkit action with
<allow_active>yes</allow_active>(no password for an active local session). It verified the update and then installed it via two separate opens of a caller-controlled path:$PKG/$SIGlive where that user can write them. Any process running as the desktop user can swap the file contents betweengpg --verifyanddpkg -i, and dpkg's maintainer scripts then run as root.dpkg -iinstalls an older, still-validly-signed release, rolling the app back to a known-vulnerable version. (rpm -Ualready refuses downgrades.)Fix
mktemp -d(0700) before verifying, then verifies and installs the copies. The unprivileged caller cannot alter bytes inside a root-only directory, so the verify→install window is closed.dpkg --compare-versions "$NEWVER" gt "$CUR"and exits otherwise. rpm keepsrpm -U(already downgrade-refusing).Test
LinuxUpdateHelperTestasserts the security-relevant structure of the generated script: package/signature copied into the work dir before--verify, verification runs against the in-work-dir copy, and the deb path usesdpkg --compare-versions … gtand exits on a non-upgrade — so a future refactor cannot silently drop the hardening.Verified locally with JDK 21:
:plugin:test --tests …LinuxUpdateHelperTestpasses. The bash logic was reviewed, not executed here; it continues to be exercised against real distros in the packaging workflows.🤖 Generated with Claude Code