fix(19.6.1-04): refuse code-declared role capture and wrap Administrators Runs

- Roles create and update refuse a submitted code that code-declared grants attach to
- compileActionExtensions wraps contributed Administrators Runs with guardOutranked
- Prove both holes on Postgres; keep the boot smoke callable with a superuser principal

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-10-22 13:29:38 +02:00
parent 422bd1e01f
commit 068adaabf9
5 changed files with 143 additions and 3 deletions

View File

@@ -61,3 +61,31 @@ func TestActionExtensionRun(t *testing.T) {
hidden := env.seedAdmin(t, adminSeed{Name: "ext-hidden", Superuser: true})
env.expectRefusal(t, http.StatusNotFound, http.MethodPost, userURL(hidden.ID, "actions", "acme_ping"), bothToken, map[string]any{}, "")
}
// TestActionExtensionOutranked (WR-03, D-19): a contributed record action
// on an outranking Administrators target is 403 user.outranked and Run is
// not called. Built-in deactivate stays 403 on that target. A covered
// target still runs.
func TestActionExtensionOutranked(t *testing.T) {
env := newScreensEnv(t)
delegate := env.seedAdmin(t, adminSeed{Name: "extout-delegate", RolePermissions: map[string]int{cabana.PermissionManageUsers: 1, pingPermission: 1}})
delegateToken := env.login(t, delegate.Login)
target := env.seedAdmin(t, adminSeed{Name: "extout-target", RolePermissions: map[string]int{"acme.demo.access": 1}})
ping := userURL(target.ID, "actions", "acme_ping")
env.expectRefusal(t, http.StatusForbidden, http.MethodPost, ping, delegateToken, map[string]any{}, enOutranked)
if runs, _ := env.spy.runs(); len(runs) != 0 {
t.Fatalf("outranked acme_ping ran: %v", runs)
}
env.expectRefusal(t, http.StatusForbidden, http.MethodPost, userURL(target.ID, "actions", "deactivate"), delegateToken, map[string]any{}, enOutranked)
covered := env.seedAdmin(t, adminSeed{Name: "extout-covered"})
result := dataOf(env.expect(t, http.StatusOK, http.MethodPost, userURL(covered.ID, "actions", "acme_ping"), delegateToken, map[string]any{}))
if result["message"] != "Pinged" {
t.Fatalf("covered result = %v", result)
}
runs, withTx := env.spy.runs()
if len(runs) != 1 || runs[0] != uint64(covered.ID) || !withTx[0] {
t.Fatalf("runs = %v tx = %v, want one run on %d inside the transaction", runs, withTx, covered.ID)
}
}