Skip to content

fix: harden auto-updater (signature verification, pinned download host) + minor supply-chain tidy-ups#70

Open
Owen-Interruptive wants to merge 2 commits into
KartikLabhshetwar:mainfrom
Owen-Interruptive:fix/harden-auto-updater
Open

fix: harden auto-updater (signature verification, pinned download host) + minor supply-chain tidy-ups#70
Owen-Interruptive wants to merge 2 commits into
KartikLabhshetwar:mainfrom
Owen-Interruptive:fix/harden-auto-updater

Conversation

@Owen-Interruptive

Copy link
Copy Markdown

What

Hardens the in-app auto-updater and tidies a couple of related supply-chain weak spots found during a security review of the codebase.

1. Auto-updater: verify before install (AppUpdater.swift)

Previously installUpdate mounted the downloaded DMG and copied the first .app it found straight over the running bundle — no signature, checksum, or notarization check. That means anyone able to tamper with the release (compromised account, malicious asset) gets silent arbitrary code execution on every install at next launch.

This PR:

  • Pins the download URL — assets are only accepted from https://github.com/KartikLabhshetwar/better-shot/releases/download/...; a tampered browser_download_url in the release JSON can no longer redirect the binary download elsewhere.
  • Verifies the downloaded bundle before swapping it in — it must pass codesign --verify --deep --strict and have the same Team ID as the running app. If the running app is unsigned (e.g. a local make build), the updater refuses to install rather than trusting any DMG — failing closed seemed like the right default. Official signed releases update normally.

2. Shell-string interpolation in relaunch helpers

relaunchApp (AppUpdater.swift) and promptRestart (BetterShotDelegate.swift) interpolated the app path into a /bin/sh -c "... open \"path\"" string. A path containing " or $(...) would be interpreted by the shell. Both now pass the path as a $0 positional argument — same behavior, no interpolation.

3. Landing page: pin "latest" dependencies

Six deps in bettershot-landing/package.json were specified as "latest", so any upstream publish would be auto-adopted on a non-frozen install. Pinned each to the version already resolved in pnpm-lock.yaml (no lockfile change needed).

Testing

  • make build succeeds; app runs normally (menu bar, capture, hotkeys).
  • Updater on an ad-hoc-signed local build correctly rejects installing with "current app is unsigned; cannot establish a trust anchor for updates".

Notes

  • One behavior change to be aware of: unsigned/ad-hoc builds can no longer auto-update themselves — intentional, but flagging it.
  • Related (out of scope here): make ship references scripts/release.sh, which isn't in the repo, so the release/signing pipeline isn't auditable from source. Committing it (or a sanitized version) would close the loop on the supply-chain story.

- Reject release assets not hosted on this repo's GitHub releases URL
- Require downloaded .app to pass codesign --verify --deep --strict and
  match the running app's Team ID before installing (refuses to update
  unsigned builds rather than silently trusting any DMG)
- Pass app paths to /bin/sh as $0 positional args instead of string
  interpolation in both relaunch helpers
@vercel

vercel Bot commented Jun 10, 2026

Copy link
Copy Markdown

@Owen-Interruptive is attempting to deploy a commit to the knox projects Team on Vercel.

A member of the Team first needs to authorize it.

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