carlos / platform Public

console: switch off skips explicit-region validation; resolveNewAppRegion rejects non-region codes (nearest-region §9.6)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 69a842241aa1858bb4082a16195cef1c2edc3794 parent 28e42e5
3 files changed, +48 −1
  • internal/console/apicli.go +4 −1
  • internal/console/regionclaim.go +5 −0
  • internal/console/regionclaim_test.go +39 −0
diff --git a/internal/console/apicli.go b/internal/console/apicli.go
index a4b740f63..be1239d2e 100644
--- a/internal/console/apicli.go
+++ b/internal/console/apicli.go
@@ -412,7 +412,10 @@ func (s *Server) handleAPICreateApp(w http.ResponseWriter, r *http.Request) {
// The region is resolved inside claimApp, once it knows the claim is not
// an adoption. The timezone and the address only ever feed that guess.
rq := regionReq{tz: req.Timezone, ip: s.personIP(r)}
- if strings.TrimSpace(req.Region) != "" {
+ // With the switch off an explicit region is neither validated nor
+ // refused (spec §9.6): resolveNewAppRegion stores the home as "default",
+ // and a refusal there would offer choices that would not be honoured.
+ if strings.TrimSpace(req.Region) != "" && s.appRegionsOn() {
code, ref := s.validateExplicitRegion(r.Context(), req.Region)
if ref != nil {
jsonError(w, ref.Status, ref.Msg)
diff --git a/internal/console/regionclaim.go b/internal/console/regionclaim.go
index e5c32cafc..b5fb535e5 100644
--- a/internal/console/regionclaim.go
+++ b/internal/console/regionclaim.go
@@ -107,6 +107,11 @@ func (s *Server) resolveNewAppRegion(ctx context.Context, rq regionReq, adopting
return "", "default"
}
if rq.hasExplicit {
+ // Defence in depth for a future door that forgets to validate: a
+ // code that is not a region never reaches the row. "" is the home.
+ if rq.explicit != "" && !provision.ValidRegion(rq.explicit) {
+ return "", "default"
+ }
return rq.explicit, "chosen"
}
if adopting {
diff --git a/internal/console/regionclaim_test.go b/internal/console/regionclaim_test.go
index 7f3914d7e..1da50338c 100644
--- a/internal/console/regionclaim_test.go
+++ b/internal/console/regionclaim_test.go
@@ -226,3 +226,42 @@ func TestClaimHeldApexWritesNothing(t *testing.T) {
t.Fatalf("a refused claim logged a region line: %s", logs.String())
}
}
+
+// Controller ruling: with the switch off the API door neither validates nor
+// refuses an explicit region (spec §9.6, C13b is the CLI's word for it). It
+// stores the home as "default", with no bucket reads for a refusal.
+func TestAPICreateAppSwitchOffIgnoresAnExplicitRegion(t *testing.T) {
+ fx, token := regionFixture(t)
+ fx.srv.cfg.AppRegions = false
+ for _, region := range []string{"nz", "Bad/Code"} {
+ name := "off" + strings.ToLower(strings.ReplaceAll(region, "/", ""))
+ status, res := postJSON(t, newAppURL(fx), map[string]string{"name": name, "region": region}, token)
+ if status != http.StatusOK || res["region"] != "ie" || res["region_source"] != "default" {
+ t.Fatalf("switch off, region %q = %d %v, want 200 home/default", region, status, res)
+ }
+ if e := errOf(res); e != "" {
+ t.Errorf("switch off refused: %q", e)
+ }
+ if r, src, _ := fx.st.AppRegion(OpsAccountID, name); r != "" || src != "default" {
+ t.Fatalf("switch off, region %q stored %q %q", region, r, src)
+ }
+ }
+}
+
+// Defence in depth for future doors: an explicit code that is not a valid
+// region never reaches the row.
+func TestResolveNewAppRegionRejectsAGarbageExplicitCode(t *testing.T) {
+ fx, _ := regionFixture(t)
+ for _, bad := range []string{"Bad/Code", "../x", "UK", "a b"} {
+ if r, src := fx.srv.resolveNewAppRegion(context.Background(), regionReq{explicit: bad, hasExplicit: true}, false); r != "" || src != "default" {
+ t.Errorf("explicit %q resolved to %q %q, want the home as default", bad, r, src)
+ }
+ }
+ // Valid choices still stand: a coded region, and the home's "".
+ if r, src := fx.srv.resolveNewAppRegion(context.Background(), regionReq{explicit: "uk", hasExplicit: true}, false); r != "uk" || src != "chosen" {
+ t.Errorf("explicit uk = %q %q", r, src)
+ }
+ if r, src := fx.srv.resolveNewAppRegion(context.Background(), regionReq{explicit: "", hasExplicit: true}, false); r != "" || src != "chosen" {
+ t.Errorf("explicit home = %q %q", r, src)
+ }
+}