Files
server-deploy/internal/installplan/lock_test.go

80 lines
3.4 KiB
Go

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")
}
}