carlos / platform Public

settings-1 task 10: resign binds the account on legacy pointers

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
claude agent, operated by paul@keymail.dev 7cb633b704f4b1276fbae6d8085d8244d2a2dc32 parent d36e4cf
2 files changed, +86 −0
  • internal/release/resign.go +7 −0
  • internal/release/resign_test.go +79 −0
diff --git a/internal/release/resign.go b/internal/release/resign.go
index 3df884f2d..26045383e 100644
--- a/internal/release/resign.go
+++ b/internal/release/resign.go
@@ -78,6 +78,9 @@ func (sg Signer) claimRawResign(ctx context.Context, s Store, plan *ResignPlan,
if plan == nil {
return errors.New("resign: no plan to sign")
}
+ if !ValidAccount(plan.Account) || !ValidApp(plan.App) || !ValidChannel(plan.Channel) {
+ return fmt.Errorf("resign: invalid %s/%s/%s", plan.Account, plan.App, plan.Channel)
+ }
// Checked again at signing: the marker may have been turned off since
// the plan was shown.
if err := resignMarker(ctx, s, sg, plan.Account); err != nil {
@@ -96,6 +99,10 @@ func (sg Signer) claimRawResign(ctx context.Context, s Store, plan *ResignPlan,
return fmt.Errorf("resign: existing pointer at %s: %w", key, err)
}
next := *old
+ // A pointer from before the account binding carries no account, which the
+ // verifier accepts and the signer refuses. A resigned pointer is a new
+ // signed statement, so it is bound to the account it was verified under.
+ next.Account = plan.Account
next.Digest, next.HistoryDigests = plan.Digest, plan.HistoryDigests
if len(next.HistoryDigests) == 0 {
next.HistoryDigests = nil
diff --git a/internal/release/resign_test.go b/internal/release/resign_test.go
index 9ec390d6a..de3f5b07b 100644
--- a/internal/release/resign_test.go
+++ b/internal/release/resign_test.go
@@ -2,6 +2,9 @@ package release
import (
"context"
+ "crypto/ed25519"
+ "encoding/base64"
+ "encoding/json"
"errors"
"fmt"
"strings"
@@ -151,6 +154,82 @@ func TestResignRequiresTheVerifiedMarker(t *testing.T) {
}
}
+// 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"); 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 {