fix(security): Ed25519-signed auto-update, replacing pipe-to-shell (CWE-494)#1
Open
nullarch wants to merge 3 commits into
Open
fix(security): Ed25519-signed auto-update, replacing pipe-to-shell (CWE-494)#1nullarch wants to merge 3 commits into
nullarch wants to merge 3 commits into
Conversation
…WE-494) The session_start auto-updater fetched the installer and executed it with no integrity check (`curl … | bash`, `iwr … | iex`) — a standing RCE channel: a compromised vibemon.dev or GitHub release would run arbitrary code on every user within 24h. The only validation was a <=16-char length check on the version string. Reported by Sascha Heyer. The updater now downloads the installer + a detached Ed25519 signature, verifies the bytes against a public key baked into the client, and executes ONLY on a valid signature — fail-closed on an invalid/missing signature or an unconfigured (placeholder) key. The signing seed lives ONLY in the VIBEMON_SIGNING_SEED GitHub Actions secret and is never committed. - src/ed25519.py: pure-stdlib Ed25519 verify/sign (RFC 8032 reference) — no new client prerequisite (python3 already required), cross-platform - src/verify_main.py + src/release_pubkey.py: client verifier + baked-in pubkey - scripts/keygen.py / scripts/sign.py: one-time keygen + release-time signing - src/notify.sh, src/notify.py: download → verify → exec (all three unsigned exec paths removed: Unix bash, Windows powershell, python client) - tests/test_signature.py: RFC 8032 KAT, tamper-rejection, placeholder fail-closed, PyNaCl cross-check - SECURITY.md / README.md: document signing; disclose auto-update + telemetry Verified end-to-end on real Linux (Docker): valid release runs, tampered and unsigned artifacts are refused. 178 tests pass; dist reproducible. Ships fail-closed (auto-update stays off until keygen + secret are set). NOTE: the CI workflow changes (release.yml signing step, test.yml pynacl) are a follow-up commit — they need the `workflow` OAuth scope to push. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01He8wXLDfXZSoyjf9wrdo3y
Member
Author
|
Reviewed the crypto and exec path end-to-end — correct and fail-closed:
Great work — this closes the CWE-494 channel cleanly. 🎉 Follow-ups to actually activate signed releases (tracked separately, not blocking): run |
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.
Summary
Closes the standing unsigned self-update RCE channel (CWE-494) reported by Sascha Heyer. The
session_startauto-updater fetched the installer and ran it with no integrity check (curl … | bash,iwr … | iex); the only validation was a ≤16-char length check on the version string. A compromisedvibemon.devor GitHub release would run arbitrary code on every user within 24h.Fix
The updater now downloads the installer + a detached Ed25519 signature, verifies it against a public key baked into the client, and executes ONLY on a valid signature — fail-closed on invalid/missing signature or an unconfigured (placeholder) key. The signing seed lives only in the
VIBEMON_SIGNING_SEEDGitHub Actions secret and is never committed, so a swapped artifact can't be forged into a valid signature. All three unsigned exec paths are removed (Unix bash, Windows PowerShell, python client).src/ed25519.py— pure-stdlib Ed25519 (RFC 8032 reference); no new client prerequisite (python3 already required), cross-platformsrc/verify_main.py+src/release_pubkey.py— client verifier + baked-in pubkey (default = all-zero placeholder ⇒ fail-closed)scripts/keygen.py/scripts/sign.py— one-time keygen + release-time signingsrc/notify.sh,src/notify.py— download → verify → exectests/test_signature.py— RFC 8032 known-answer, tamper-rejection (5 cases), placeholder fail-closed, PyNaCl cross-checkSECURITY.md/README.md— document signing; disclose auto-update + telemetry at the install promptVerification
dist/reproducible;bash -ncleanShips fail-closed
Auto-update stays off (no unsigned exec possible) until the maintainer runs
scripts/keygen.py, sets theVIBEMON_SIGNING_SEEDsecret, andvibemon.devserves the.sigfiles.workflowscopeThe CI changes (
release.ymlsigning step,test.ymlpynacl) are in a separate local commit not yet pushed — pushing.github/workflows/needs theworkflowOAuth scope. Grant withgh auth refresh -s workflow, then push.🤖 Generated with Claude Code