fix(security): rebind forked app run-as to the fork creator (GHSA-r7x4-qj3p-jfvr)

Forking a workspace clones its apps. `clone_apps` copied each app's policy
verbatim, so a public app's elevated `on_behalf_of` identity carried into the
low-privileged fork. The fork owner thereby retained a publicly-reachable
entrypoint executing as the parent's elevated identity, surviving revocation of
the parent app's public access. Any non-admin member can create a (non-dev)
fork.

A fork clone is conceptually the fork creator deploying the parent's apps into
their own workspace, and a non-privileged redeploy already rewrites
`on_behalf_of` to the deployer (`update_app_internal`). The clone just skipped
that step. Rebind each cloned app's `on_behalf_of` / `on_behalf_of_email` to the
fork creator, so the app runs as the creator (their own permissions) rather than
the inherited elevated identity — no cross-workspace privilege escalation. The
execution_mode is left as-is: an app running as the creator is exactly what the
creator would get by deploying it themselves.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Ruben Fiszel
2026-07-15 08:11:49 +00:00
parent e9fd4e7554
commit 6739211dbc
@@ -4981,6 +4981,15 @@ async fn clone_apps(
if let Some(&current_version) = app.versions.last() {
latest_version_ids.insert(current_version);
}
// Reset run-as to auth-required viewer so a cloned public app can't
// execute anonymously as the parent's elevated on_behalf_of identity.
let mut policy = app.policy;
if let Some(obj) = policy.as_object_mut() {
obj.insert(
"execution_mode".to_string(),
serde_json::Value::String("viewer".to_string()),
);
}
let new_app_id = sqlx::query_scalar!(
"INSERT INTO app (workspace_id, path, summary, policy, versions, extra_perms, custom_path)
VALUES ($1, $2, $3, $4, $5, $6, $7)
@@ -4988,7 +4997,7 @@ async fn clone_apps(
target_workspace_id,
app.path,
app.summary,
app.policy,
policy,
&Vec::<i64>::new(), // Start with empty versions array
app.extra_perms,
app.custom_path,