carlos / platform Public

Prove the app token's refusal rests on its app binding, not on absence

Paul Campbell pushed by paul@keymail.dev 19ccbc5f046e9f4ec2f650dc55cfd35291714086 parent 73247bb
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(),