Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ export function UpdateReviewBody({ state, onReload }: any) {
await client.appUpdate.applyUpdate({
appId: state.app.common.identity.id,
additionalGrantedPermissions,
reviewedManifestHash: state.app.pendingUpdate?.manifestHash ?? '',
});

await close();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ use crate::{
pub struct AppUpdateApplyUpdateParams {
pub app_id: String,
pub additional_granted_permissions: SageGrantedPermissionsInput,
/// Manifest hash of the pending update the user actually reviewed. Apply is
/// rejected if the pending update no longer matches this hash.
pub reviewed_manifest_hash: String,
}

#[derive(Debug, Clone, Serialize, Type)]
Expand Down Expand Up @@ -56,6 +59,7 @@ impl BridgeMethod for AppUpdateApplyUpdate {
tools.host_state,
&params.app_id,
Some(params.additional_granted_permissions),
Some(params.reviewed_manifest_hash),
)
.await
.map_err(|err| {
Expand Down
16 changes: 15 additions & 1 deletion crates/sage-apps/src/lifecycle/update/apply.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ pub(crate) async fn apply_app_update_inner(
apps_state: &State<'_, AppsHostState>,
app_id: &str,
additional_granted_permissions_input: Option<SageGrantedPermissionsInput>,
expected_manifest_hash: Option<String>,
) -> Result<SageAppView> {
let _update_guard = apps_state
.try_begin_app_update(app_id)
Expand All @@ -40,6 +41,19 @@ pub(crate) async fn apply_app_update_inner(
return Ok(resolved.with_app(|app| app.into()));
}

// When applying a reviewed update, bind the apply to the exact manifest the
// user reviewed. If the pending update changed in between (e.g. the
// background checker fetched a newer manifest), refuse to apply the reviewed
// permission grants to a manifest the user never saw.
if let Some(expected) = expected_manifest_hash.as_deref()
&& preflight.pending.manifest_hash() != expected
{
return Err(io::Error::other(format!(
"pending update for {app_id} changed since it was reviewed; re-check the update before applying"
))
.into());
}

let reopen_after_update = ReopenAfterUpdate::capture(app_handle, apps_state, app_id).await?;

let app = execute_app_update(
Expand Down Expand Up @@ -110,7 +124,7 @@ pub(crate) async fn try_auto_apply_pending_update(
return Ok(false);
}

apply_app_update_inner(app_handle, apps_state, app_id, None).await?;
apply_app_update_inner(app_handle, apps_state, app_id, None, None).await?;

Ok(true)
}
Expand Down
2 changes: 1 addition & 1 deletion crates/sage-apps/src/lifecycle/update/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,5 +22,5 @@ pub async fn apps_apply_app_update(
apps_state: State<'_, AppsHostState>,
app_id: String,
) -> Result<SageAppView> {
apply_app_update_inner(&app_handle, &apps_state, &app_id, None).await
apply_app_update_inner(&app_handle, &apps_state, &app_id, None, None).await
}
Loading