| index 4d4552ac9..0621cdaf1 100644 |
| --- a/internal/release/proofgate_test.go |
| +++ b/internal/release/proofgate_test.go |
| @@ -492,3 +492,113 @@ func TestProofGateNilIsOff(t *testing.T) { |
| t.Fatal("a nil gate refused the staged bytes") |
| } |
| } |
| + |
| +// bindingRefusal is TestProofBindingRefusesStagedBytesThatDiffer's first |
| +// pass: a proven manifest, other bytes staged, a RefusalNotProven written. |
| +func bindingRefusal(t *testing.T) *proofEnv { |
| + t.Helper() |
| + e := newProofEnv(t) |
| + e.requireDev(t) |
| + if err := StageArtifact(e.opt, "bab", "new", "console", []byte("not the shipped bytes")); err != nil { |
| + t.Fatal(err) |
| + } |
| + e.putAt(t, e.newSHA, e.signed(t, e.prover, "console", e.newSHA, "dev")) |
| + e.pass(t, e.store) |
| + if r, _ := ReadRefusal(e.bake, proofHost); r == nil || r.Kind != RefusalNotProven || e.live() != "old" { |
| + t.Fatalf("refusal = %+v, live = %q; want a not-proven refusal and old", r, e.live()) |
| + } |
| + return e |
| +} |
| + |
| +// Final review H3: removing the require entry, or the whole policy file, is |
| +// the break glass, and it works after a binding refusal as well as after a |
| +// missing-proof wait. The stored D3 record stops blocking and is cleared. |
| +func TestProofBreakGlassOverridesABindingRefusal(t *testing.T) { |
| + for name, lift := range map[string]func(t *testing.T, e *proofEnv){ |
| + "require entry removed": func(t *testing.T, e *proofEnv) { e.writePolicy(t, `{"schema":1}`) }, |
| + "policy file removed": func(t *testing.T, e *proofEnv) { |
| + if err := os.Remove(e.policy); err != nil { |
| + t.Fatal(err) |
| + } |
| + }, |
| + } { |
| + t.Run(name, func(t *testing.T) { |
| + e := bindingRefusal(t) |
| + lift(t, e) |
| + out := e.pass(t, e.store) |
| + if e.live() != "new" { |
| + t.Fatalf("live = %q, printed %q; want new once the box no longer requires a proof", e.live(), out) |
| + } |
| + if r, _ := ReadRefusal(e.bake, proofHost); r != nil { |
| + t.Fatalf("refusal = %+v, want it cleared", r) |
| + } |
| + if strings.Contains(out, "refused:") { |
| + t.Fatalf("printed %q: the D3 refusal still gated the pass", out) |
| + } |
| + }) |
| + } |
| +} |
| + |
| +// A policy that will not load is closed: the D3 record keeps blocking, even |
| +// on a route with no unit, which admit alone would let through. |
| +func TestProofBrokenPolicyKeepsABindingRefusal(t *testing.T) { |
| + e := bindingRefusal(t) |
| + e.writePolicy(t, `{"schema":1}}`) |
| + out := e.pass(t, e.store) |
| + if e.live() != "old" || !strings.Contains(out, "refused: new's staged bytes do not match dev's proof") { |
| + t.Fatalf("live = %q, printed %q; want old and the not-proven gate line", e.live(), out) |
| + } |
| + if r, _ := ReadRefusal(e.bake, proofHost); r == nil || r.Kind != RefusalNotProven { |
| + t.Fatalf("refusal = %+v, want the not-proven record kept", r) |
| + } |
| +} |
| + |
| +// D1 and D2 records never depend on the policy: lifting the requirement |
| +// leaves them blocking. |
| +func TestProofBreakGlassLeavesOtherRefusals(t *testing.T) { |
| + for _, kind := range []string{RefusalNotUp, RefusalWrongProgram} { |
| + t.Run("kind "+kind, func(t *testing.T) { |
| + e := bindingRefusal(t) |
| + r, _ := ReadRefusal(e.bake, proofHost) |
| + r.Kind = kind |
| + if err := WriteRefusal(e.bake, *r); err != nil { |
| + t.Fatal(err) |
| + } |
| + if err := os.Remove(e.policy); err != nil { |
| + t.Fatal(err) |
| + } |
| + out := e.pass(t, e.store) |
| + if e.live() != "old" || !strings.Contains(out, "refused: new") { |
| + t.Fatalf("live = %q, printed %q; want old and the refusal gate", e.live(), out) |
| + } |
| + if got, _ := ReadRefusal(e.bake, proofHost); got == nil || got.Kind != kind { |
| + t.Fatalf("refusal = %+v, want the %q record kept", got, kind) |
| + } |
| + }) |
| + } |
| +} |
| + |
| +func TestProofGateRequires(t *testing.T) { |
| + var nilGate *ProofGate |
| + if req, perr := nilGate.Requires("console"); req || perr { |
| + t.Fatalf("nil gate Requires = %v, %v; want false, false", req, perr) |
| + } |
| + e := newProofEnv(t) |
| + e.gate.BeginPass() |
| + if req, perr := e.gate.Requires("console"); req || perr { |
| + t.Fatalf("no policy: Requires = %v, %v; want false, false", req, perr) |
| + } |
| + e.requireDev(t) |
| + e.gate.BeginPass() |
| + if req, perr := e.gate.Requires("console"); !req || perr { |
| + t.Fatalf("listed: Requires = %v, %v; want true, false", req, perr) |
| + } |
| + if req, perr := e.gate.Requires("other"); req || perr { |
| + t.Fatalf("unlisted: Requires = %v, %v; want false, false", req, perr) |
| + } |
| + e.writePolicy(t, `{"schema":1,"require":[`) |
| + e.gate.BeginPass() |
| + if req, perr := e.gate.Requires("other"); !req || !perr { |
| + t.Fatalf("broken policy: Requires = %v, %v; want true, true", req, perr) |
| + } |
| +} |