Skip to content

Fix (high): bind applyUpdate to the reviewed manifest hash#797

Open
Rigidity wants to merge 1 commit into
appsfrom
fix/apps-update-bind-reviewed-manifest
Open

Fix (high): bind applyUpdate to the reviewed manifest hash#797
Rigidity wants to merge 1 commit into
appsfrom
fix/apps-update-bind-reviewed-manifest

Conversation

@Rigidity

@Rigidity Rigidity commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Severity: High (H4)

Problem

appUpdate.applyUpdate re-reads whatever pending update is currently staged on the app (preflight.pending) and applies the user's reviewed permission grants (additionalGrantedPermissions) to it. Between appUpdate.getReviewContext (what the user reviews) and applyUpdate (what runs), the background update checker can replace the pending update with a newer manifest requesting different permissions. The grants the user reviewed then get applied to a manifest they never saw — a review/apply TOCTOU.

Fix

  • AppUpdateApplyUpdateParams gains reviewed_manifest_hash. apply_app_update_inner takes an expected_manifest_hash: Option<String> and, when set, refuses to apply if preflight.pending.manifest_hash() no longer matches (error asks the user to re-check).
  • Automatic/background apply paths (try_auto_apply_pending_update, apps_apply_app_update command) pass None and are unaffected.
  • The update-review builtin app forwards state.app.pendingUpdate.manifestHash (the manifest whose decision/permissions it displayed).

Files

  • crates/sage-apps/src/lifecycle/update/apply.rs, .../update/commands.rs
  • crates/sage-apps/src/bridge/methods/system/app_update/apply_update.rs
  • builtin-apps/src/system/apps/app-update/src/components/UpdateReviewBody.tsx

No local build (per request); relying on CI. (SDK generated-types.ts is gitignored and regenerated from the Rust types.)


Note

Medium Risk
Touches permission-granting update apply logic, but the change tightens security (hash binding) and leaves non-interactive apply paths unchanged.

Overview
Closes a review/apply TOCTOU: user-confirmed permission grants could be applied to a newer pending manifest if the background update checker swapped the staged update between review and applyUpdate.

appUpdate.applyUpdate now requires reviewedManifestHash. apply_app_update_inner compares it to the current pending update’s manifest hash and fails with a message to re-check when they differ. The update-review UI sends state.app.pendingUpdate.manifestHash on confirm.

Auto-apply paths (try_auto_apply_pending_update, apps_apply_app_update) pass None for the hash and behave as before.

Reviewed by Cursor Bugbot for commit 0554b49. Bugbot is set up for automated code reviews on this repo. Configure here.

appUpdate.applyUpdate re-read whatever pending update was currently
staged on the app and applied the user's reviewed permission grants to
it. Between appUpdate.getReviewContext and applyUpdate the background
update checker can replace the pending update with a newer manifest that
requests different permissions, so the grants the user reviewed could be
applied to a manifest they never saw.

Require applyUpdate callers to pass the reviewed manifest hash and refuse
to apply if the current pending update no longer matches it. The
automatic/background apply paths pass None and are unaffected. The update
review builtin app now forwards the pending update's manifest hash.
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