package host import ( "context" "crypto/sha256" "encoding/hex" "encoding/json" "testing" "github.com/carlosframework/platform/internal/hostclaim" "github.com/carlosframework/platform/internal/hostguard" ) func TestPlan14BoundProgram(t *testing.T) { f, _, _ := generationLaunchFixture(t) b, _, _ := plan14UnitBindingFixture(t, "console.service", "", "") in, err := f.root.Guard.Root.Installation(t.Context(), f.target.Host, hostguard.Captured{Incarnation: f.target.Incarnation, Stamp: f.target.Stamp}) if err != nil { t.Fatal(err) } p := claimUnitProgram{Schema: 1, Host: in.Host, Args: []string{"console", "-listen", "127.0.0.1:8090"}, Env: []string{"CONSOLE_ORIGIN=https://shop.example"}} raw, _ := json.Marshal(p) sum := sha256.Sum256(raw) s := claimUnitShape{Host: in.Host, Account: in.Account, App: in.App, Kind: "instance", Unit: "console.service", Addr: "127.0.0.1:8090", Program: hex.EncodeToString(sum[:])} b.installation = func(context.Context, string) (hostguard.RootInstallation, error) { return in, nil } b.canonical = func(context.Context, string) (claimUnitShape, error) { return s, nil } if _, err := b.establish(t.Context(), in.Host); err != nil { t.Fatal(err) } f.root.unitBindings = b f.root.unitProgram = func(shape claimUnitShape) (claimUnitProgram, error) { return decodeClaimUnitProgram(raw, shape) } f.root.Units = plan14NamedLaunchUnits{f.units, in.Host, s.Unit} f.req.Spec.Args, f.req.Spec.Env = p.Args, p.Env f.req.Spec.Identity.Config = s.Program launch, err := f.root.launchSpec(f.req) if err != nil || len(launch.Argv) != 4 || launch.Argv[1] != "console" { t.Fatal(launch, err) } f.units.started.Unit, f.units.started.ControlGroup, _ = f.root.Units.(plan14NamedLaunchUnits).Placement(in.Host) f.req.Reservation.Group.RelativePath = f.units.started.ControlGroup inputs, err := f.req.Spec.Hash() if err != nil { t.Fatal(err) } f.req.Reservation.Inputs = inputs f.claims.set(func(c *hostclaim.Claim) { c.Holder.WakeRetry.Attempt.LaunchInputs = inputs }) f.units.beforeCheck = func() { raw = append(raw, ' ') } if _, err := f.root.Serve(t.Context(), f.peer, f.req); err == nil { t.Fatal("changed program at exec accepted") } if n, _ := f.units.counts(); n != 0 { t.Fatal("changed program executed") } raw, _ = json.Marshal(p) f.req.Spec.Env = []string{"CONSOLE_ORIGIN=https://forged.example"} if _, err := f.root.launchSpec(f.req); err == nil { t.Fatal("service-selected environment accepted") } f.req.Spec.Env = p.Env f.req.Spec.Identity.Config = "forged-config" if _, err := f.root.launchSpec(f.req); err == nil { t.Fatal("service-selected program identity accepted") } f.req.Spec.Identity.Config = s.Program f.req.Spec.Args = []string{"shell"} if _, err := f.root.launchSpec(f.req); err == nil { t.Fatal("service-selected command accepted") } f.req.Spec.Args = p.Args raw = append(raw, ' ') if _, err := f.root.launchSpec(f.req); err == nil { t.Fatal("changed canonical bytes accepted") } f.root.unitProgram = nil if _, err := f.root.launchSpec(f.req); err == nil { t.Fatal("missing program reader fell through") } } func TestPlan14BoundProgramDecode(t *testing.T) { for _, p := range []claimUnitProgram{ {Schema: 2, Host: "shop.example"}, {Schema: 1, Host: "sibling.example"}, {Schema: 1, Host: "shop.example", Args: []string{"bad\x00arg"}}, {Schema: 1, Host: "shop.example", Env: []string{"A=1", "A=2"}}, {Schema: 1, Host: "shop.example", Env: []string{"INVALID"}}, } { raw, _ := json.Marshal(p) sum := sha256.Sum256(raw) if _, err := decodeClaimUnitProgram(raw, claimUnitShape{Host: "shop.example", Program: hex.EncodeToString(sum[:])}); err == nil { t.Fatal("invalid program accepted", p) } } }