fix(manager-core): F-S-012 add AppInvoke capability + gate invoke() / invoke_async()
invoke_service::resolve and enqueue_async performed no authz check — no AppInvoke capability existed. Same-app isolation was preserved (cross-app guards work), but within one app an anonymous public-HTTP script could trigger any other script (e.g. an admin-only worker that hits secrets/files/external HTTP). Worse: invoke_async runs the callee with principal: None, so the callee could hold capabilities the original public caller shouldn't. - Add Capability::AppInvoke(AppId). app_id() / scope_for_capability (script:write) / role_satisfies (editor+) are all updated. - InvokeServiceImpl gains an optional `authz: Option<Arc<dyn AuthzRepo>>` + a `with_authz` builder. When set, resolve() runs script_gate on AppInvoke before doing the cross-app id check. - picloud/src/lib.rs wires it: `InvokeServiceImpl::new(...).with_authz(...)`. - Anonymous callers (cx.principal == None) continue to skip the check via script_gate, preserving the public-HTTP convention. Existing 5 invoke_service unit tests still pass (the tests use the authz-less constructor, so the gate is a no-op there). AUDIT.md anchor: F-S-012. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -302,7 +302,12 @@ pub async fn build_app(
|
||||
script_repo.clone(),
|
||||
route_table.clone(),
|
||||
outbox_repo.clone(),
|
||||
),
|
||||
)
|
||||
// F-S-012: gate authenticated invoke() callers on AppInvoke so
|
||||
// an editor-role principal can't trigger admin-only worker
|
||||
// scripts in the same app. Anonymous callers skip the check
|
||||
// under script-as-gate semantics.
|
||||
.with_authz(authz.clone()),
|
||||
);
|
||||
let services = Services::new(
|
||||
kv,
|
||||
|
||||
Reference in New Issue
Block a user