Prove the app token's refusal rests on its app binding, not on absence
1 file changed,
+24
−2
internal/console/apisidecars_test.go+24 −2
diff --git a/internal/console/apisidecars_test.go b/internal/console/apisidecars_test.go| index fd3d8e8f8..69e7f19f6 100644 |
| --- a/internal/console/apisidecars_test.go |
| +++ b/internal/console/apisidecars_test.go |
| @@ -77,6 +77,14 @@ func TestAPISidecarRefusalMatrix(t *testing.T) { |
| if _, err := fx.st.CreateAppIn(42, "titogo", "bob@keymail.dev", now); err != nil { |
| t.Fatal(err) |
| } |
| + // A real SIBLING app in the token's own account, with sidecars enabled on |
| + // it. The foreign-app refusal must rest on the token's app binding, not on |
| + // the app being absent or the feature being off: if app scoping broke, |
| + // every gate past this point would let the call through to a create. |
| + if _, err := fx.st.CreateAppIn(OpsAccountID, "sibling", "alice@keymail.dev", now); err != nil { |
| + t.Fatal(err) |
| + } |
| + enableSidecars(t, fx, "sibling", titoSidecarManifest()) |
| appTok, err := fx.st.NewAppToken(OpsAccountID, "titogo", "alice@keymail.dev", "signup", now) |
| if err != nil { |
| t.Fatal(err) |
| @@ -99,8 +107,16 @@ func TestAPISidecarRefusalMatrix(t *testing.T) { |
| {"app token, foreign account", http.MethodGet, sidecarsURL(fx, "cdef", "titogo"), appTok, ""}, |
| {"app token, foreign account create", http.MethodPost, sidecarsURL(fx, "cdef", "titogo"), appTok, body}, |
| {"app token, foreign account wake", http.MethodPost, sidecarsURL(fx, "cdef", "titogo") + "/bot/wake", appTok, ""}, |
| - {"app token, foreign app", http.MethodGet, sidecarsURL(fx, "bab", "other"), appTok, ""}, |
| - {"app token, foreign app create", http.MethodPost, sidecarsURL(fx, "bab", "other"), appTok, body}, |
| + {"app token, unclaimed app", http.MethodGet, sidecarsURL(fx, "bab", "other"), appTok, ""}, |
| + {"app token, unclaimed app create", http.MethodPost, sidecarsURL(fx, "bab", "other"), appTok, body}, |
| + // The rigorous half: a REAL sibling app in the token's own account, |
| + // with sidecars enabled on it, so nothing but the binding can be |
| + // doing the refusing. |
| + {"app token, sibling app in its own account", http.MethodGet, sidecarsURL(fx, "bab", "sibling"), appTok, ""}, |
| + {"app token, sibling app create", http.MethodPost, sidecarsURL(fx, "bab", "sibling"), appTok, body}, |
| + {"app token, sibling app get", http.MethodGet, sidecarsURL(fx, "bab", "sibling") + "/bot", appTok, ""}, |
| + {"app token, sibling app delete", http.MethodDelete, sidecarsURL(fx, "bab", "sibling") + "/bot", appTok, ""}, |
| + {"app token, sibling app wake", http.MethodPost, sidecarsURL(fx, "bab", "sibling") + "/bot/wake", appTok, ""}, |
| // The account segment resolves by NAME for a user token; an app |
| // token holds no memberships to resolve one against, so its own |
| // account's name is not a way in either. |
| @@ -137,6 +153,12 @@ func TestAPISidecarRefusalMatrix(t *testing.T) { |
| provision.SidecarRecordKey("cdef", "titogo", "bot")); err == nil { |
| t.Fatalf("a record exists in the neighbouring account after only refused creates") |
| } |
| + // And nothing landed in the sibling app, whose sidecars ARE enabled: the |
| + // app token reached neither the manifest nor the write. |
| + if _, _, err := fx.rel.Get(context.Background(), |
| + provision.SidecarRecordKey("bab", "sibling", "bot")); err == nil { |
| + t.Fatalf("an app token wrote into a SIBLING app in its own account — the binding is not scoping by app") |
| + } |
| // And no wake object was minted by a refused wake — the wake write is |
| // strictly behind the gate and behind the record's own existence. |
| if _, _, err := fx.rel.Get(context.Background(), |