| index 6b778e15c..182ce0da0 100644 |
| --- a/internal/release/laddercheck_test.go |
| +++ b/internal/release/laddercheck_test.go |
| @@ -1,16 +1,21 @@ |
| package release_test |
| |
| import ( |
| + "context" |
| + "encoding/json" |
| "testing" |
| |
| "github.com/carlosframework/platform/internal/release" |
| ) |
| |
| +// liveChannels is what production is running on RIGHT NOW. |
| +var liveChannels = []string{"canary/platform-adoption", "canary/keymail", "canary/console", "canary/rehearsal"} |
| + |
| // The two channel names production is running on RIGHT NOW must never be |
| // refused by the ladder — go.tito.io follows canary/platform-adoption and |
| // keymail.dev follows canary/keymail. If these fail, production cannot deploy. |
| func TestLiveProductionChannelsAreNeverRefused(t *testing.T) { |
| - for _, ch := range []string{"canary/platform-adoption", "canary/keymail", "canary/console", "canary/rehearsal"} { |
| + for _, ch := range liveChannels { |
| rung, ok := release.Rung(ch) |
| if !ok || rung != release.RungCanary { |
| t.Fatalf("Rung(%q) = %q,%v — want canary,true", ch, rung, ok) |
| @@ -36,6 +41,68 @@ func TestEdgeToStableIsRefusedAndNamesTheBypass(t *testing.T) { |
| } |
| } |
| |
| +// The other half of "production can still deploy": every live channel is |
| +// promoted with the ROOT key, and what lands is a wrapper with NO grant in |
| +// it, verifying against the root public key. Scoped signing is opt-in from |
| +// the first byte — nothing about the fleet's existing pointers changes until |
| +// someone deliberately mints a scoped key. |
| +// |
| +// Mutation-mindedness: make the grant field non-optional, attach an empty |
| +// grant to root-signed pointers, or route root signing through grant |
| +// handling, and this fails on the key list — before any signature check, and |
| +// for every channel production actually runs on. |
| +func TestLiveProductionChannelsStayRootSignedWithNoGrant(t *testing.T) { |
| + ctx := context.Background() |
| + s := release.NewDirStore(t.TempDir()) |
| + seedB64, pubB64, err := release.GenerateKey() |
| + if err != nil { |
| + t.Fatalf("GenerateKey: %v", err) |
| + } |
| + priv, err := release.ParseSigningKey(seedB64, "test") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + pub, err := release.ParsePublicKey(pubB64, "test") |
| + if err != nil { |
| + t.Fatal(err) |
| + } |
| + m := release.Manifest{ |
| + App: "kass", Version: "9f3c1ad", Kind: "binary", |
| + Artifacts: map[string]release.Artifact{"linux-arm64": {}}, |
| + } |
| + if err := release.Ship(ctx, s, release.OpsAccount, m, map[string][]byte{"linux-arm64": []byte("build")}); err != nil { |
| + t.Fatalf("Ship: %v", err) |
| + } |
| + |
| + for _, ch := range liveChannels { |
| + if err := release.Promote(ctx, s, priv, release.OpsAccount, "kass", ch, "9f3c1ad"); err != nil { |
| + t.Fatalf("Promote %s: %v", ch, err) |
| + } |
| + raw, _, err := s.Get(ctx, release.ChannelKey(release.OpsAccount, "kass", ch)) |
| + if err != nil { |
| + t.Fatalf("read %s: %v", ch, err) |
| + } |
| + var keys map[string]json.RawMessage |
| + if err := json.Unmarshal(raw, &keys); err != nil { |
| + t.Fatalf("%s: %v", ch, err) |
| + } |
| + if len(keys) != 2 || keys["payload"] == nil || keys["sig"] == nil { |
| + t.Errorf("%s wrapper has keys %v, want exactly payload and sig", ch, keysOf(keys)) |
| + } |
| + if _, err := release.Channel(ctx, s, pub, release.OpsAccount, "kass", ch); err != nil { |
| + t.Errorf("%s does not verify against the root pubkey: %v", ch, err) |
| + } |
| + } |
| +} |
| + |
| +func keysOf(m map[string]json.RawMessage) []string { |
| + out := make([]string, 0, len(m)) |
| + for k := range m { |
| + out = append(out, k) |
| + } |
| + return out |
| +} |
| + |
| func contains(s, sub string) bool { |
| for i := 0; i+len(sub) <= len(s); i++ { |
| if s[i:i+len(sub)] == sub { |