package installplan import ( "crypto/sha256" "encoding/hex" "encoding/json" "errors" "regexp" "strings" ) type Lock struct { ProtocolVersion int `json:"protocolVersion"` Repository string `json:"repository"` Suite string `json:"suite"` Architecture string `json:"architecture"` ReleaseDigest string `json:"releaseDigest"` Packages []Package `json:"packages"` } type Package struct { Name string `json:"name"` Version string `json:"version"` Filename string `json:"filename"` Digest string `json:"digest"` Size uint64 `json:"size"` } var versionPattern = regexp.MustCompile(`^(?:[0-9]+:)?[0-9][0-9A-Za-z.+~-]*$`) var digestPattern = regexp.MustCompile(`^sha256:[0-9a-f]{64}$`) var required = []string{"docker-ce", "docker-ce-cli", "containerd.io", "docker-buildx-plugin", "docker-compose-plugin"} // Validate checks a caller-supplied lock against a fixed source policy. This is // NOT repository signature validation or evidence these artifacts exist. func Validate(lock Lock, suite, architecture string) (string, error) { reject := errors.New("invalid Docker package lock") if lock.ProtocolVersion != 1 || lock.Repository != "https://download.docker.com/linux/ubuntu" || lock.Suite != suite || lock.Architecture != architecture { return "", reject } if (suite != "jammy" && suite != "noble" && suite != "resolute") || (architecture != "amd64" && architecture != "arm64") || !digestPattern.MatchString(lock.ReleaseDigest) || len(lock.Packages) != len(required) { return "", reject } versions := map[string]string{} for _, p := range lock.Packages { allowed := false for _, name := range required { if p.Name == name { allowed = true } } if !allowed || versions[p.Name] != "" || len(p.Version) > 128 || !versionPattern.MatchString(p.Version) || !digestPattern.MatchString(p.Digest) || p.Size == 0 || p.Size > 512<<20 { return "", reject } fileVersion := p.Version if _, after, ok := strings.Cut(fileVersion, ":"); ok { fileVersion = after } if p.Filename != "dists/"+suite+"/pool/stable/"+architecture+"/"+p.Name+"_"+fileVersion+"_"+architecture+".deb" { return "", reject } versions[p.Name] = p.Version } if versions["docker-ce"] != versions["docker-ce-cli"] { return "", reject } raw, _ := json.Marshal(lock) sum := sha256.Sum256(raw) return "sha256:" + hex.EncodeToString(sum[:]), nil }