package release import ( "os" "path/filepath" "testing" "time" ) func TestPlan14ForgedHealthyCannotAdmit(t *testing.T) { for _, renamed := range []bool{false, true} { name := "original" if renamed { name = "parent-replaced" } t.Run(name, func(t *testing.T) { e := newProofEnv(t) e.requireDev(t) e.gate = NewRootProofGate(e.policy, e.bake).WithClock(func() time.Time { return e.clock }) if err := RecordHealthy(e.bake, "bab", "console", HealthyBuild{SHA256: e.newSHA, How: "live", At: e.clock}); err != nil { t.Fatal(err) } if renamed { // Rename the whole parent, then plant a valid, attacker-owned replacement. dir := filepath.Join(e.bake, "healthy") if err := os.Rename(dir, dir+"-old"); err != nil { t.Fatal(err) } if err := RecordHealthy(e.bake, "bab", "console", HealthyBuild{SHA256: e.newSHA, How: "settle", At: e.clock}); err != nil { t.Fatal(err) } } e.pass(t, e.store) if got := e.live(); got != "old" { t.Fatalf("forged history adopted %q", got) } // A separately verified proof still opens the same gate. e.putAt(t, e.newSHA, e.signed(t, e.prover, "console", e.newSHA, "dev")) e.clock = e.clock.Add(ProofMissTTL + time.Second) e.pass(t, e.store) if got := e.live(); got != "new" { t.Fatalf("signed proof refused: %q", got) } }) } } func TestPlan14ForgedHealthyCannotBind(t *testing.T) { for _, renamed := range []bool{false, true} { name := "original" if renamed { name = "parent-replaced" } t.Run(name, func(t *testing.T) { e := newProofEnv(t) e.requireDev(t) g := NewRootProofGate(e.policy, e.bake) if err := StageArtifact(e.opt, "bab", "new", "console", []byte("bad build")); err != nil { t.Fatal(err) } if err := RecordHealthy(e.bake, "bab", "console", HealthyBuild{SHA256: e.newSHA, How: "live", At: e.clock}); err != nil { t.Fatal(err) } if renamed { dir := filepath.Join(e.bake, "healthy") if err := os.Rename(dir, dir+"-old"); err != nil { t.Fatal(err) } if err := RecordHealthy(e.bake, "bab", "console", HealthyBuild{SHA256: e.newSHA, How: "settle", At: e.clock}); err != nil { t.Fatal(err) } } rt, err := e.reg.Get(proofHost) if err != nil || rt == nil { t.Fatalf("%v", err) } if g.bind(e.opt, *rt, &ChannelPointer{Version: "new"}, proofVerdict{required: true, sha: e.newSHA}, "old", e.clock) { t.Fatal("bind trusted agent history") } }) } } func TestPlan14RootHistoryStrictShape(t *testing.T) { for _, raw := range []string{ `{"schema":1,"builds":[]} {}`, `{"schema":1,"extra":true,"builds":[]}`, `{"schema":1,"builds":[{"sha256":"fake","how":"live","at":"2026-10-12T00:00:00Z"}]}`, `{"schema":2,"builds":[]}`, } { if _, err := decodeRootHealthy([]byte(raw)); err == nil { t.Fatalf("accepted %s", raw) } } if b, err := decodeRootHealthy([]byte(`{"schema":1,"builds":[]}`)); err != nil || len(b) != 0 { t.Fatalf("%v %v", b, err) } } func TestPlan14HealthyErasureRequiresProof(t *testing.T) { for _, target := range []string{"record", "parent"} { t.Run(target, func(t *testing.T) { e := newProofEnv(t) e.requireDev(t) e.gate = NewRootProofGate(e.policy, e.bake).WithClock(func() time.Time { return e.clock }) if err := RecordHealthy(e.bake, "bab", "console", HealthyBuild{SHA256: e.newSHA, How: "live", At: e.clock}); err != nil { t.Fatal(err) } path := filepath.Join(e.bake, "healthy", "bab", "console.json") if target == "parent" { path = filepath.Join(e.bake, "healthy") } if err := os.RemoveAll(path); err != nil { t.Fatal(err) } e.pass(t, e.store) if e.live() != "old" { t.Fatal("erased history granted adoption") } rt, err := e.reg.Get(proofHost) if err != nil || rt == nil { t.Fatal(err) } if e.gate.bind(e.opt, *rt, &ChannelPointer{Version: "new"}, proofVerdict{required: true, sha: e.newSHA}, "old", e.clock) { t.Fatal("erased history granted binding") } e.putAt(t, e.newSHA, e.signed(t, e.prover, "console", e.newSHA, "dev")) e.clock = e.clock.Add(ProofMissTTL + time.Second) e.pass(t, e.store) if e.live() != "new" { t.Fatal("signed positive refused after erasure") } }) } }