package host import ( "context" "encoding/json" "testing" "time" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" "github.com/carlosframework/platform/internal/provision" "github.com/carlosframework/platform/internal/registry" "github.com/carlosframework/platform/internal/release" ) func TestPlan14RootDispatchRejectsRegistryMutation(t *testing.T) { t.Parallel() t.Run("protected-passes", testPlan14RootProtectedDispatch) for _, attack := range []string{"none", "account", "app", "unit", "backing", "delete", "record owner", "claim owner", "missing installation"} { t.Run(attack, func(t *testing.T) { store := release.NewDirStore(t.TempDir()) rec := provision.Record{Schema: 1, Host: "shop.example.com", Account: "bab", App: "shop", Channel: "stable", CreatedAt: time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC)} plan14PutRecord(t, store, rec) inc, err := hostclaim.Incarnation(rec.Account, rec.App, rec.Host, rec.CreatedAt) if err != nil { t.Fatal(err) } claim := &hostclaim.Claim{Host: rec.Host, Holder: &hostclaim.Holder{Key: provision.RecordKey(rec.Account, rec.App, rec.Host), Account: rec.Account, App: rec.App, Incarnation: inc, Stamp: 1, Session: 1, WriterBox: "box"}} g := hostguard.Guard{DataDir: t.TempDir(), LockDir: t.TempDir(), Claims: rootViewClaimReader{obs: hostclaim.Observed{Host: rec.Host, ETag: "claim", Claim: claim}}, Root: hostguard.NewRootAuthority(&rollbackMemory{files: map[string][]byte{}})} if attack != "missing installation" { in := hostguard.RootInstallation{Host: rec.Host, Incarnation: inc, Account: rec.Account, App: rec.App, Stamp: 1, Migration: 1, Record: claim.Holder.Key, Box: "box", Enrollment: "enroll"} if err := g.EstablishRoot(t.Context(), in, func(*hostclaim.Claim) error { return nil }); err != nil { t.Fatal(err) } } reg := restartTestRegistry(t) row := registry.Route{Host: rec.Host, Account: rec.Account, App: rec.App} switch attack { case "account": row.Account = "bap" case "app": row.App = "other" case "unit": row.Unit = "other.service" case "backing": row.Backing = registry.BackingUnit case "record owner": rec.App = "other" plan14PutRecord(t, store, rec) case "claim owner": claim.Holder.App = "other" } if attack != "delete" { if err := reg.Upsert(row); err != nil { t.Fatal(err) } } acct, app, err := rootInstallationHostApp(g, store)(rec.Host) refused := attack == "record owner" || attack == "claim owner" || attack == "missing installation" if refused { if err == nil { t.Fatal("unbound root dispatch") } return } if err != nil || acct != "bab" || app != "shop" { t.Fatalf("root installation dispatch: %q %q %v", acct, app, err) } }) } } type rootViewClaimReader struct{ obs hostclaim.Observed } func (r rootViewClaimReader) Read(context.Context, string) (hostclaim.Observed, error) { return r.obs, nil } func TestPlan14RootSidecarBinding(t *testing.T) { for _, attack := range []string{"none", "account", "app", "host", "name", "key", "incarnation", "missing"} { t.Run(attack, func(t *testing.T) { store := release.NewDirStore(t.TempDir()) rec := provision.SidecarRecord{Schema: 1, Name: "worker", Host: provision.SidecarHost("worker", "shop", "bab"), Account: "bab", App: "shop", Channel: "stable", CreatedAt: time.Date(2026, 10, 1, 0, 0, 0, 0, time.UTC)} key := provision.SidecarRecordKey(rec.Account, rec.App, rec.Name) inc, err := hostclaim.Incarnation(rec.Account, rec.App, rec.Host, rec.CreatedAt) if err != nil { t.Fatal(err) } c := &hostclaim.Claim{Host: rec.Host, Holder: &hostclaim.Holder{Family: "sidecar", Key: key, Name: rec.Name, Account: rec.Account, App: rec.App, Incarnation: inc}} switch attack { case "account": rec.Account = "other" case "app": rec.App = "other" case "host": rec.Host = "other.example" case "name": rec.Name = "other" case "key": c.Holder.Key = "apps/other/shop/sidecars/worker.json" case "incarnation": c.Holder.Incarnation = "other" } if attack != "missing" { body, _ := json.Marshal(rec) if _, err := store.Put(t.Context(), key, body, ""); err != nil { t.Fatal(err) } } row, etag, err := rootClaimRecord(t.Context(), store, c, t.TempDir()) if attack != "none" { if err == nil { t.Fatal("forged sidecar bound") } return } if err != nil || etag == "" || !row.IsSidecar() || row.Account != "bab" || row.App != "shop" || row.Socket != "" || row.Addr != "" { t.Fatal(row, etag, err) } }) } }