package release import ( "context" "crypto/ed25519" "encoding/base64" "encoding/json" "errors" "fmt" "strings" "testing" "time" ) func TestResignSignsTheDigestsTheOperatorSaw(t *testing.T) { s := NewDirStore(t.TempDir()) ctx := context.Background() priv, pub := testKeys(t) shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one")) shipRelease(t, s, "bab", "km", "v2", adoptPlatform, []byte("two")) for _, v := range []string{"v1", "v2"} { if err := Promote(ctx, s, priv, "bab", "km", "stable", v); err != nil { t.Fatal(err) } } markerOn(t, s, priv, "bab") // every writer upgraded; the old pointer still has no digests sg := RootSigner(priv) plan, err := sg.PlanResign(ctx, s, "bab", "km", "stable") if err != nil { t.Fatal(err) } if plan.Digest != manifestDigestOf(t, s, "bab", "km", "v2") || plan.HistoryDigests[0] != manifestDigestOf(t, s, "bab", "km", "v1") || !plan.Computed[0] || !plan.Computed[1] { t.Fatalf("plan = %+v", plan) } if p := readPtr(t, s, pub, "stable"); p.Digest != "" { t.Fatal("PlanResign wrote something") } if err := sg.Resign(ctx, s, plan, "ops@example.com"); err != nil { t.Fatal(err) } p := readPtr(t, s, pub, "stable") if p.Version != "v2" || p.Digest != plan.Digest || p.HistoryDigests[0] != plan.HistoryDigests[0] { t.Fatalf("resigned pointer = %+v", p) } // Once resigned, the rollback the spec refused is allowed. if err := Rollback(ctx, s, priv, "bab", "km", "stable"); err != nil { t.Fatalf("rollback after resign: %v", err) } } func TestResignCopiesSignedDigestsAndLeavesGoneBuildsEmpty(t *testing.T) { s := NewDirStore(t.TempDir()) ctx := context.Background() priv, _ := testKeys(t) shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one")) shipRelease(t, s, "bab", "km", "v2", adoptPlatform, []byte("two")) shipRelease(t, s, "bab", "km", "v3", adoptPlatform, []byte("three")) if err := Promote(ctx, s, priv, "bab", "km", "stable", "v1"); err != nil { t.Fatal(err) } if err := Promote(ctx, s, priv, "bab", "km", "stable", "v2"); err != nil { t.Fatal(err) } markerOn(t, s, priv, "bab") if err := Promote(ctx, s, priv, "bab", "km", "stable", "v3"); err != nil { t.Fatal(err) } if err := DeleteVersion(ctx, s, "bab", "km", "v1"); err != nil { t.Fatal(err) } plan, err := RootSigner(priv).PlanResign(ctx, s, "bab", "km", "stable") if err != nil { t.Fatal(err) } // head v3 signed, v2 inherited "" (computed now), v1 gone. if plan.Computed[0] || !plan.Computed[1] || plan.HistoryDigests[1] != "" || plan.Computed[2] { t.Fatalf("plan = %+v", plan) } } func TestResignRefusesAChannelThatMoved(t *testing.T) { s := NewDirStore(t.TempDir()) ctx := context.Background() priv, _ := testKeys(t) shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one")) shipRelease(t, s, "bab", "km", "v2", adoptPlatform, []byte("two")) if err := Promote(ctx, s, priv, "bab", "km", "stable", "v1"); err != nil { t.Fatal(err) } markerOn(t, s, priv, "bab") sg := RootSigner(priv) plan, err := sg.PlanResign(ctx, s, "bab", "km", "stable") if err != nil { t.Fatal(err) } if err := Promote(ctx, s, priv, "bab", "km", "stable", "v2"); err != nil { t.Fatal(err) } if err := sg.Resign(ctx, s, plan, "ops"); !errors.Is(err, ErrResignChanged) { t.Fatalf("resign over a moved channel = %v, want ErrResignChanged", err) } } func TestResignRefusesAHeadWithNoManifest(t *testing.T) { s := NewDirStore(t.TempDir()) ctx := context.Background() priv, _ := testKeys(t) shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one")) if err := Promote(ctx, s, priv, "bab", "km", "stable", "v1"); err != nil { t.Fatal(err) } if err := DeleteVersion(ctx, s, "bab", "km", "v1"); err != nil { t.Fatal(err) } markerOn(t, s, priv, "bab") if _, err := RootSigner(priv).PlanResign(ctx, s, "bab", "km", "stable"); err == nil || err.Error() != fmt.Sprintf(TextResignUnreadable, "v1") { t.Fatalf("a head with no manifest was planned: %v", err) } } // Astra I3 #4: resign arms every box's ratchet for the channel, so it needs // the verified marker (every writer upgraded), at plan time and at signing. func TestResignRequiresTheVerifiedMarker(t *testing.T) { s := NewDirStore(t.TempDir()) ctx := context.Background() priv, pub := testKeys(t) shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one")) if err := Promote(ctx, s, priv, "bab", "km", "stable", "v1"); err != nil { t.Fatal(err) } sg := RootSigner(priv) if _, err := sg.PlanResign(ctx, s, "bab", "km", "stable"); err == nil || err.Error() != fmt.Sprintf(TextResignNoMarker, "bab", "bab") { t.Fatalf("PlanResign with no marker = %v, want the no-marker refusal", err) } markerOn(t, s, priv, "bab") plan, err := sg.PlanResign(ctx, s, "bab", "km", "stable") if err != nil { t.Fatal(err) } if err := RemoveDigestMarker(ctx, s, "bab"); err != nil { // turned off between plan and confirmation t.Fatal(err) } if err := sg.Resign(ctx, s, plan, "ops"); err == nil || err.Error() != fmt.Sprintf(TextResignNoMarker, "bab", "bab") { t.Fatalf("Resign with the marker gone = %v, want the no-marker refusal", err) } foreign, _ := testKeys(t) markerOn(t, s, foreign, "bab") // present, but not root-signed if err := sg.Resign(ctx, s, plan, "ops"); err == nil || !strings.Contains(err.Error(), "marker") { t.Fatalf("Resign with an unverifiable marker = %v", err) } if p := readPtr(t, s, pub, "stable"); p.Digest != "" { t.Fatal("a refused resign wrote digests") } } // unbindPointer rewrites a channel's pointer as one signed before the account // binding existed: same fields, no account, root-signed. func unbindPointer(t *testing.T, s Store, priv ed25519.PrivateKey, acct, app, channel string) { t.Helper() ctx := context.Background() key := ChannelKey(acct, app, channel) body, etag, err := GetQualified(ctx, s, key) if err != nil { t.Fatal(err) } var w signedWrapper if err := json.Unmarshal(body, &w); err != nil { t.Fatal(err) } payload, err := base64.StdEncoding.DecodeString(w.Payload) if err != nil { t.Fatal(err) } var p ChannelPointer if err := json.Unmarshal(payload, &p); err != nil { t.Fatal(err) } p.Account = "" payload, err = json.MarshalIndent(&p, "", " ") if err != nil { t.Fatal(err) } b64 := base64.StdEncoding.EncodeToString(payload) wire, err := json.MarshalIndent(signedWrapper{Payload: b64, Sig: base64.StdEncoding.EncodeToString(ed25519.Sign(priv, []byte(b64)))}, "", " ") if err != nil { t.Fatal(err) } if _, err := s.Put(ctx, key, wire, etag); err != nil { t.Fatal(err) } } // A channel not promoted since the account binding has no account in its // pointer. Resign targets exactly those, and must bind the account. func TestResignBindsTheAccountOnAPreBindingPointer(t *testing.T) { s := NewDirStore(t.TempDir()) ctx := context.Background() priv, pub := testKeys(t) shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one")) shipRelease(t, s, "bab", "km", "v2", adoptPlatform, []byte("two")) for _, v := range []string{"v1", "v2"} { if err := Promote(ctx, s, priv, "bab", "km", "stable", v); err != nil { t.Fatal(err) } } unbindPointer(t, s, priv, "bab", "km", "stable") if p := readPtr(t, s, pub, "stable"); p.Account != "" { t.Fatalf("test setup: pointer still bound to %q", p.Account) } markerOn(t, s, priv, "bab") sg := RootSigner(priv) plan, err := sg.PlanResign(ctx, s, "bab", "km", "stable") if err != nil { t.Fatal(err) } if err := sg.Resign(ctx, s, plan, "ops"); err != nil { t.Fatalf("resign of a pre-binding pointer: %v", err) } p := readPtr(t, s, pub, "stable") if p.Account != "bab" || p.Digest != plan.Digest || p.HistoryDigests[0] != plan.HistoryDigests[0] { t.Fatalf("resigned pointer = %+v", p) } } func TestResignRefusesAnInvalidPlan(t *testing.T) { priv, _ := testKeys(t) if err := RootSigner(priv).claimRawResign(context.Background(), NewDirStore(t.TempDir()), &ResignPlan{Account: "bab", App: "km", Channel: "../x"}, "ops", nil); err == nil { t.Fatal("a plan with an invalid channel was signed") } } func TestResignRefusesANilPlan(t *testing.T) { priv, _ := testKeys(t) if err := RootSigner(priv).Resign(context.Background(), NewDirStore(t.TempDir()), nil, "ops"); err == nil { t.Fatal("a nil plan was signed") } } func TestDigestStateOf(t *testing.T) { at := time.Date(2026, 10, 7, 12, 0, 0, 0, time.UTC) m := &DigestMarker{Schema: 1, Account: "bab", At: at} legacyBefore := &ChannelPointer{Channel: "a", Version: "v1", PromotedAt: at.Add(-time.Hour), History: []string{"v0"}} legacyAfter := &ChannelPointer{Channel: "b", Version: "v1", PromotedAt: at.Add(time.Hour)} signed := &ChannelPointer{Channel: "c", Version: "v2", Digest: dA, PromotedAt: at.Add(time.Hour), History: []string{"v1"}, HistoryDigests: []string{""}} if st := DigestStateOf(legacyBefore, m); st.HeadDigest || st.Straggler || st.HistoryMissing != 1 { t.Fatalf("before the marker: %+v", st) } if st := DigestStateOf(legacyAfter, m); !st.Straggler { t.Fatalf("written after the marker without digests: %+v", st) } if st := DigestStateOf(legacyAfter, nil); st.Straggler { t.Fatal("no marker, no stragglers") } if st := DigestStateOf(signed, m); !st.HeadDigest || st.Straggler || st.HistoryMissing != 1 { t.Fatalf("signed: %+v", st) } } func TestDigestReportListsEveryChannel(t *testing.T) { s := NewDirStore(t.TempDir()) ctx := context.Background() priv, pub := testKeys(t) shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one")) if err := Promote(ctx, s, priv, "bab", "km", "beta", "v1"); err != nil { t.Fatal(err) } markerOn(t, s, priv, "bab") if err := Promote(ctx, s, priv, "bab", "km", "canary/x", "v1"); err != nil { t.Fatal(err) } rows, marker, err := DigestReport(ctx, s, pub, "bab", "km") if err != nil || marker == nil || len(rows) != 2 { t.Fatalf("report = %+v, %+v, %v", rows, marker, err) } got := map[string]bool{} for _, r := range rows { got[r.Channel] = r.HeadDigest } if got["beta"] || !got["canary/x"] { t.Fatalf("report = %+v", rows) } } // Astra r1 #2: ResignAuthorized asks the caller's authority of the pipeline // read inside the mutation, after the pointer; a refusal is an // AuthorizationError and the pointer is untouched. With no hook (Resign) the // same plan signs. func TestResignAuthorizedAsksTheHookAndWritesNothingOnRefusal(t *testing.T) { s := NewDirStore(t.TempDir()) ctx := context.Background() priv, pub := testKeys(t) shipRelease(t, s, "bab", "km", "v1", adoptPlatform, []byte("one")) if err := Promote(ctx, s, priv, "bab", "km", "stable", "v1"); err != nil { t.Fatal(err) } if _, err := SetPipeline(ctx, s, priv, "bab", "km", []PipelineChannel{{Name: "stable", Rules: ChannelRules{Passkey: true}}}, "t"); err != nil { t.Fatal(err) } markerOn(t, s, priv, "bab") sg := RootSigner(priv) plan, err := sg.PlanResign(ctx, s, "bab", "km", "stable") if err != nil { t.Fatal(err) } refusal := errors.New("needs a passkey") var saw *Pipeline err = sg.ResignAuthorized(ctx, s, plan, "ops@example.com", func(p *Pipeline) error { saw = p return refusal }) var ae *AuthorizationError if !errors.As(err, &ae) || !errors.Is(ae.Err, refusal) { t.Fatalf("ResignAuthorized = %v, want the hook's refusal as an AuthorizationError", err) } if saw == nil || len(saw.Channels) != 1 || !saw.Channels[0].Rules.Passkey { t.Fatalf("the hook saw pipeline %+v, want the app's", saw) } if p := readPtr(t, s, pub, "stable"); p.Digest != "" { t.Fatalf("a refused resign wrote %+v", p) } if err := sg.ResignAuthorized(ctx, s, plan, "ops@example.com", func(*Pipeline) error { return nil }); err != nil { t.Fatalf("an allowing hook: %v", err) } if p := readPtr(t, s, pub, "stable"); p.Digest != plan.Digest { t.Fatalf("after an allowed resign = %+v", p) } }