package release import ( "bytes" "context" "crypto/ed25519" "encoding/json" "errors" "fmt" "os" "path/filepath" "runtime" "sync" "syscall" "testing" "github.com/carlosframework/platform/internal/registry" ) const ( heldTestHost = "held.example.com" heldTestOp = "mv_abcdefghijklmnopqrstuvwxyz" ) // heldPriv signs every held fixture's pointers and promotion records, and // heldPub verifies them: the release key a held stage is handed. var ( heldPriv = ed25519.NewKeyFromSeed(bytes.Repeat([]byte{7}, ed25519.SeedSize)) heldPub = heldPriv.Public().(ed25519.PublicKey) ) // heldFixture ships two builds of bab/console and moves the stable channel // to the newer one: the pinned release (v1) is deliberately NOT the head. // v1 was promoted to canary/x before that, so it has a promotion record and // a held stage of it is §4.1-authorized (settings spec rev 11 §4.3a). func heldFixture(t *testing.T) (context.Context, Store, string) { t.Helper() ctx := context.Background() s := NewDirStore(t.TempDir()) shipRelease(t, s, "bab", "console", "v1", adoptPlatform, []byte("pinned build")) shipRelease(t, s, "bab", "console", "v2", adoptPlatform, []byte("channel head")) if err := Promote(ctx, s, heldPriv, "bab", "console", "canary/x", "v1"); err != nil { t.Fatalf("Promote: %v", err) } if err := Promote(ctx, s, heldPriv, "bab", "console", "stable", "v2"); err != nil { t.Fatalf("Promote: %v", err) } return ctx, s, t.TempDir() } func heldRoute() registry.Route { return registry.Route{Host: heldTestHost, Kind: "instance", App: "console", Account: "bab", Channel: "stable", Status: "landing", HoldOp: heldTestOp} } // TestMaterializeHeldStagesThePinnedVersion: the held row runs exactly the // pinned release, never the channel head, and LiveVersion reads it back. func TestMaterializeHeldStagesThePinnedVersion(t *testing.T) { ctx, s, opt := heldFixture(t) m, err := MaterializeHeld(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), openGuard) if err != nil { t.Fatalf("MaterializeHeld: %v", err) } if m == nil || m.Version != "v1" { t.Fatalf("manifest = %+v, want v1's", m) } if got, err := LiveVersion(opt, heldTestHost); err != nil || got != "v1" { t.Fatalf("LiveVersion = %q, %v; want the pinned v1, not the channel head", got, err) } body, err := os.ReadFile(LiveLink(opt, heldTestHost)) if err != nil || string(body) != "pinned build" { t.Fatalf("live binary = %q, %v", body, err) } } // TestMaterializeHeldStageBlocked: a process that cannot write the version // store gets ErrStageBlocked wrapping the cause and writes nothing. EROFS is // what ProtectSystem=strict answers, and it is not fs.ErrPermission. func TestMaterializeHeldStageBlocked(t *testing.T) { for _, errno := range []syscall.Errno{syscall.EROFS, syscall.EACCES} { t.Run(errno.Error(), func(t *testing.T) { ctx, s, opt := heldFixture(t) orig := materializeHeldStage t.Cleanup(func() { materializeHeldStage = orig }) materializeHeldStage = func(context.Context, Store, string, string, string, string, string, string) (*Manifest, error) { return nil, &os.PathError{Op: "mkdir", Path: filepath.Join(opt, "versions"), Err: errno} } _, err := MaterializeHeld(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), openGuard) if !errors.Is(err, ErrStageBlocked) || !errors.Is(err, errno) { t.Fatalf("MaterializeHeld: %v, want ErrStageBlocked wrapping %v", err, errno) } if ents, _ := os.ReadDir(opt); len(ents) != 0 { t.Fatalf("optDir is not empty: %v", ents) } }) } other := &os.PathError{Op: "open", Path: "x", Err: syscall.ENOENT} if StageBlocked(other) || !StageBlocked(&os.PathError{Err: syscall.EPERM}) { t.Fatal("StageBlocked classifies EROFS, EPERM and EACCES only") } } // TestMaterializeHeldRefusesWhileHostClaimIsWired mirrors RenderHeld's // ruling F10: the live link is written outside any claim selection, so with // CARLOS_HOST_CLAIM on the call refuses and writes nothing. func TestMaterializeHeldRefusesWhileHostClaimIsWired(t *testing.T) { ctx, s, opt := heldFixture(t) t.Setenv("CARLOS_HOST_CLAIM", "1") if _, err := MaterializeHeld(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), openGuard); !errors.Is(err, ErrHeldClaimWired) { t.Fatalf("MaterializeHeld: %v, want ErrHeldClaimWired", err) } if ents, _ := os.ReadDir(opt); len(ents) != 0 { t.Fatalf("optDir is not empty: %v", ents) } } // heldRecordingStore counts every call that reaches the store underneath. type heldRecordingStore struct { Store mu sync.Mutex calls []string } func (r *heldRecordingStore) note(s string) { r.mu.Lock(); r.calls = append(r.calls, s); r.mu.Unlock() } func (r *heldRecordingStore) Get(ctx context.Context, key string) ([]byte, string, error) { r.note("get " + key) return r.Store.Get(ctx, key) } func (r *heldRecordingStore) List(ctx context.Context, prefix string) ([]string, error) { r.note("list " + prefix) return r.Store.List(ctx, prefix) } func writeHeldRequest(t *testing.T, dataDir, name string, req HeldStageRequest) string { t.Helper() dir := HeldStageDir(dataDir) if err := os.MkdirAll(dir, 0o755); err != nil { t.Fatal(err) } raw, err := json.Marshal(req) if err != nil { t.Fatal(err) } path := filepath.Join(dir, name) if err := os.WriteFile(path, raw, 0o644); err != nil { t.Fatal(err) } return path } func goodHeldRequest() HeldStageRequest { return HeldStageRequest{Account: "bab", App: "console", Host: heldTestHost, Version: "v1", Op: heldTestOp, Box: "uk-box"} } func gone(t *testing.T, path string) { t.Helper() if _, err := os.Lstat(path); !os.IsNotExist(err) { t.Fatalf("%s is still there: %v", path, err) } } // TestServeHeldStageRequestsRefusesStaleAndTraversal: the root side of the // held-stage relay validates every component before any filesystem effect, // asks authorize, and only then materializes. A request it refuses is // removed and never repoints live/. func TestServeHeldStageRequestsRefusesStaleAndTraversal(t *testing.T) { t.Run("no request directory is a no-op", func(t *testing.T) { ctx, s, opt := heldFixture(t) rec := &heldRecordingStore{Store: s} dd := t.TempDir() called := false if err := ServeHeldStageRequests(ctx, rec, heldPub, nr(t), opt, dd, offGate(t), passAuth(func(HeldStageRequest) { called = true })); err != nil { t.Fatalf("ServeHeldStageRequests: %v", err) } if called || len(rec.calls) != 0 { t.Fatalf("authorize called %v, store calls %v", called, rec.calls) } if ents, _ := os.ReadDir(dd); len(ents) != 0 { t.Fatalf("dataDir gained %v", ents) } if ents, _ := os.ReadDir(opt); len(ents) != 0 { t.Fatalf("optDir gained %v", ents) } }) t.Run("an op the claim no longer names", func(t *testing.T) { ctx, s, opt := heldFixture(t) dd := t.TempDir() req := goodHeldRequest() req.Op = "mv_bcdefghijklmnopqrstuvwxyza" path := writeHeldRequest(t, dd, req.Host+".json", req) authorize := func(r HeldStageRequest, then func() error) error { if r.Op != heldTestOp { return fmt.Errorf("%w: the claim names %s", ErrStaleHeldStage, heldTestOp) } return runThen(then) } if err := ServeHeldStageRequests(ctx, s, heldPub, nr(t), opt, dd, offGate(t), authorize); err != nil { t.Fatalf("ServeHeldStageRequests: %v", err) } gone(t, path) gone(t, LiveLink(opt, heldTestHost)) gone(t, filepath.Join(HeldStagedDir(dd), heldTestHost+".json")) }) for name, tc := range map[string]struct { file string mutate func(*HeldStageRequest) }{ "host traverses": {"evil.json", func(r *HeldStageRequest) { r.Host = "../x" }}, "host is not the file name": {heldTestHost + ".json", func(r *HeldStageRequest) { r.Host = "other.example.com" }}, "host carries a NUL": {"nul.json", func(r *HeldStageRequest) { r.Host = "nul\x00" }}, "version traverses": {heldTestHost + ".json", func(r *HeldStageRequest) { r.Version = ".." }}, "account is malformed": {heldTestHost + ".json", func(r *HeldStageRequest) { r.Account = "../bab" }}, "app is malformed": {heldTestHost + ".json", func(r *HeldStageRequest) { r.App = "a/b" }}, "op is empty": {heldTestHost + ".json", func(r *HeldStageRequest) { r.Op = "" }}, "box is empty": {heldTestHost + ".json", func(r *HeldStageRequest) { r.Box = "" }}, } { t.Run(name, func(t *testing.T) { ctx, s, opt := heldFixture(t) rec := &heldRecordingStore{Store: s} dd := t.TempDir() req := goodHeldRequest() tc.mutate(&req) path := writeHeldRequest(t, dd, tc.file, req) called := false if err := ServeHeldStageRequests(ctx, rec, heldPub, nr(t), opt, dd, offGate(t), passAuth(func(HeldStageRequest) { called = true })); err != nil { t.Fatalf("ServeHeldStageRequests: %v", err) } gone(t, path) if called || len(rec.calls) != 0 { t.Fatalf("a refused request reached authorize (%v) or the store (%v)", called, rec.calls) } if ents, _ := os.ReadDir(opt); len(ents) != 0 { t.Fatalf("optDir gained %v", ents) } }) } t.Run("garbage is removed", func(t *testing.T) { ctx, s, opt := heldFixture(t) dd := t.TempDir() path := filepath.Join(HeldStageDir(dd), heldTestHost+".json") if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil { t.Fatal(err) } if err := os.WriteFile(path, []byte("{not json"), 0o644); err != nil { t.Fatal(err) } if err := ServeHeldStageRequests(ctx, s, heldPub, nr(t), opt, dd, offGate(t), passAuth(nil)); err != nil { t.Fatal(err) } gone(t, path) }) t.Run("a symlinked request is removed, never followed", func(t *testing.T) { ctx, s, opt := heldFixture(t) dd := t.TempDir() target := filepath.Join(t.TempDir(), "target.json") raw, _ := json.Marshal(goodHeldRequest()) if err := os.WriteFile(target, raw, 0o644); err != nil { t.Fatal(err) } if err := os.MkdirAll(HeldStageDir(dd), 0o755); err != nil { t.Fatal(err) } link := filepath.Join(HeldStageDir(dd), heldTestHost+".json") if err := os.Symlink(target, link); err != nil { t.Fatal(err) } called := false if err := ServeHeldStageRequests(ctx, s, heldPub, nr(t), opt, dd, offGate(t), passAuth(func(HeldStageRequest) { called = true })); err != nil { t.Fatal(err) } gone(t, link) if called { t.Fatal("a symlinked request reached authorize") } if _, err := os.Stat(target); err != nil { t.Fatalf("the link's target was touched: %v", err) } }) t.Run("a failed materialization keeps the request", func(t *testing.T) { ctx, s, opt := heldFixture(t) dd := t.TempDir() req := goodHeldRequest() req.Version = "v9" // never shipped // ...but authorized (a signed record), so the stage fails after // §4.1 lets it through, not at it: an unauthorized build is // dropped (TestServeHeldStageRequestsDropsUnauthorizedBuilds). if err := RootSigner(heldPriv).WritePromotionRecord(ctx, s, PromotionRecord{Account: "bab", App: "console", Version: "v9", ManifestDigest: ManifestDigest([]byte("v9")), Channel: "canary/x"}); err != nil { t.Fatal(err) } path := writeHeldRequest(t, dd, req.Host+".json", req) if err := ServeHeldStageRequests(ctx, s, heldPub, nr(t), opt, dd, offGate(t), passAuth(nil)); err != nil { t.Fatal(err) } if _, err := os.Stat(path); err != nil { t.Fatalf("the request was dropped after a failed stage: %v", err) } gone(t, LiveLink(opt, heldTestHost)) }) t.Run("the happy path materializes, acks and removes", func(t *testing.T) { ctx, s, opt := heldFixture(t) dd := t.TempDir() req := goodHeldRequest() path := writeHeldRequest(t, dd, req.Host+".json", req) var seen []HeldStageRequest if err := ServeHeldStageRequests(ctx, s, heldPub, nr(t), opt, dd, offGate(t), passAuth(func(r HeldStageRequest) { seen = append(seen, r) })); err != nil { t.Fatalf("ServeHeldStageRequests: %v", err) } if len(seen) != 2 || seen[0] != req || seen[1] != req { t.Fatalf("authorize saw %+v, want %+v before staging and again around publication", seen, req) } gone(t, path) if got, err := LiveVersion(opt, heldTestHost); err != nil || got != "v1" { t.Fatalf("LiveVersion = %q, %v; want v1", got, err) } raw, err := os.ReadFile(filepath.Join(HeldStagedDir(dd), heldTestHost+".json")) if err != nil { t.Fatalf("no held-staged ack: %v", err) } var ack HeldStaged if err := json.Unmarshal(raw, &ack); err != nil || ack.Version != "v1" || ack.Op != heldTestOp || ack.At.IsZero() { t.Fatalf("held-staged ack = %s (%v)", raw, err) } }) } // openGuard publishes unconditionally: the guard of a test whose authority // is not the point. func openGuard(publish func() error) error { return publish() } func runThen(then func() error) error { if then == nil { return nil } return then() } // passAuth is an authority that accepts everything, noting each call. func passAuth(note func(HeldStageRequest)) func(HeldStageRequest, func() error) error { return func(r HeldStageRequest, then func() error) error { if note != nil { note(r) } return runThen(then) } } // TestMaterializeHeldNeedsAGuard: publication without an authority would // repoint live/ on the word of a request alone, so a nil guard is // refused after staging and before anything is published. func TestMaterializeHeldNeedsAGuard(t *testing.T) { ctx, s, opt := heldFixture(t) if _, err := MaterializeHeld(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), nil); err == nil { t.Fatal("MaterializeHeld published with no guard") } gone(t, LiveLink(opt, heldTestHost)) } // TestHeldPublicationRechecksItsAuthority: the authority that admitted a // request before staging is asked again around publication, and a move // aborted or replaced while the release was staging leaves live/ // untouched, drops the request, and reports ErrStaleHeldStage. func TestHeldPublicationRechecksItsAuthority(t *testing.T) { ctx, s, opt := heldFixture(t) orig := materializeHeldStage t.Cleanup(func() { materializeHeldStage = orig }) staged := false materializeHeldStage = func(ctx context.Context, s Store, optDir, acct, app, ver, host, want string) (*Manifest, error) { m, err := orig(ctx, s, optDir, acct, app, ver, host, want) staged = true // the move is replaced while this runs return m, err } published := false authorize := func(_ HeldStageRequest, then func() error) error { if staged { return fmt.Errorf("%w: the claim names another op", ErrStaleHeldStage) } return runThen(then) } _, err := MaterializeHeld(ctx, s, heldPub, nr(t), opt, heldRoute(), "v1", offGate(t), func(publish func() error) error { return authorize(goodHeldRequest(), func() error { published = true; return publish() }) }) if !errors.Is(err, ErrStaleHeldStage) || published { t.Fatalf("MaterializeHeld: %v (published %v), want ErrStaleHeldStage and nothing published", err, published) } gone(t, LiveLink(opt, heldTestHost)) staged = false dd := t.TempDir() path := writeHeldRequest(t, dd, heldTestHost+".json", goodHeldRequest()) if err := ServeHeldStageRequests(ctx, s, heldPub, nr(t), opt, dd, offGate(t), authorize); err != nil { t.Fatal(err) } gone(t, path) gone(t, LiveLink(opt, heldTestHost)) gone(t, filepath.Join(HeldStagedDir(dd), heldTestHost+".json")) } // offGate is a D3 gate on a box with no proof policy: the held stage's // behaviour is exactly what it was before D3-held. func offGate(t *testing.T) *ProofGate { t.Helper() return NewProofGate(filepath.Join(t.TempDir(), "proof-policy.json"), t.TempDir()) } func TestHeldStageHardlinkedRequestIsRefused(t *testing.T) { if runtime.GOOS != "linux" { t.Skip("Linux descriptor boundary") } ctx, s, opt := heldFixture(t) dd := t.TempDir() target := filepath.Join(t.TempDir(), "target.json") raw, err := json.Marshal(goodHeldRequest()) if err != nil { t.Fatal(err) } if err := os.WriteFile(target, raw, 0o644); err != nil { t.Fatal(err) } if err := os.MkdirAll(HeldStageDir(dd), 0o755); err != nil { t.Fatal(err) } request := filepath.Join(HeldStageDir(dd), heldTestHost+".json") if err := os.Link(target, request); err != nil { t.Fatal(err) } called := false if err := ServeHeldStageRequests(ctx, s, heldPub, nr(t), opt, dd, offGate(t), passAuth(func(HeldStageRequest) { called = true })); err != nil { t.Fatal(err) } if called { t.Fatal("hardlinked request reached authorize") } gone(t, request) gone(t, filepath.Join(HeldStagedDir(dd), heldTestHost+".json")) after, err := os.ReadFile(target) if err != nil || string(after) != string(raw) { t.Fatalf("hardlink target changed: %q %v", after, err) } }