package installplan import ( "strings" "testing" ) func lockFixture() Lock { l := Lock{ProtocolVersion: 1, Repository: "https://download.docker.com/linux/ubuntu", Suite: "resolute", Architecture: "amd64", ReleaseDigest: "sha256:" + strings.Repeat("b", 64), Packages: []Package{}} for _, name := range []string{"docker-ce", "docker-ce-cli", "containerd.io", "docker-buildx-plugin", "docker-compose-plugin"} { l.Packages = append(l.Packages, Package{Name: name, Version: "1.2.3-1", Filename: "dists/resolute/pool/stable/amd64/" + name + "_1.2.3-1_amd64.deb", Digest: "sha256:" + strings.Repeat("a", 64), Size: 123}) } return l } func TestValidLockAndStableDigest(t *testing.T) { l := lockFixture() digest, err := Validate(l, "resolute", "amd64") if err != nil || !strings.HasPrefix(digest, "sha256:") { t.Fatal("valid lock rejected", err) } l.Packages[0].Size++ changed, err := Validate(l, "resolute", "amd64") if err != nil || changed == digest { t.Fatal("lock digest does not bind size") } } func TestRejectUnsafeLock(t *testing.T) { for name, mutate := range map[string]func(*Lock){ "protocol": func(l *Lock) { l.ProtocolVersion = 2 }, "repository": func(l *Lock) { l.Repository = "https://attacker.example/linux/ubuntu" }, "credentials": func(l *Lock) { l.Repository = "https://user:secret@download.docker.com/linux/ubuntu" }, "suite": func(l *Lock) { l.Suite = "noble" }, "architecture": func(l *Lock) { l.Architecture = "arm64" }, "release": func(l *Lock) { l.ReleaseDigest = "" }, "missing": func(l *Lock) { l.Packages = l.Packages[:4] }, "extra": func(l *Lock) { l.Packages = append(l.Packages, l.Packages[0]) }, "duplicate": func(l *Lock) { l.Packages[1] = l.Packages[0] }, "latest": func(l *Lock) { l.Packages[0].Version = "latest" }, "shell": func(l *Lock) { l.Packages[0].Version = "1;reboot" }, "wrong file": func(l *Lock) { l.Packages[0].Filename = "dists/resolute/pool/stable/amd64/other.deb" }, "traversal": func(l *Lock) { l.Packages[0].Filename = "../docker.deb" }, "encoded path": func(l *Lock) { l.Packages[0].Filename = "dists/resolute/pool/stable/amd64/%2e%2e.deb" }, "digest": func(l *Lock) { l.Packages[0].Digest = "bad" }, "size": func(l *Lock) { l.Packages[0].Size = 0 }, "engine mismatch": func(l *Lock) { l.Packages[1].Version = "2.3.4-1" l.Packages[1].Filename = "dists/resolute/pool/stable/amd64/docker-ce-cli_2.3.4-1_amd64.deb" }, } { t.Run(name, func(t *testing.T) { l := lockFixture() mutate(&l) if _, err := Validate(l, "resolute", "amd64"); err == nil { t.Fatal("unsafe lock accepted") } }) } } func TestDockerEpochAndTargetArchitecture(t *testing.T) { l := lockFixture() for i := range l.Packages { p := &l.Packages[i] if p.Name == "docker-ce" || p.Name == "docker-ce-cli" { p.Version = "5:29.1.0-1~ubuntu.26.04~resolute" p.Filename = "dists/resolute/pool/stable/amd64/" + p.Name + "_29.1.0-1~ubuntu.26.04~resolute_amd64.deb" } } if _, err := Validate(l, "resolute", "amd64"); err != nil { t.Fatal("epoch version rejected", err) } if _, err := Validate(l, "resolute", "arm64"); err == nil { t.Fatal("architecture mismatch accepted") } l.Packages[0].Filename = strings.Replace(l.Packages[0].Filename, "_29.", "_5:29.", 1) if _, err := Validate(l, "resolute", "amd64"); err == nil { t.Fatal("epoch in repository filename accepted") } }