69 lines
2.4 KiB
Go
69 lines
2.4 KiB
Go
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
|
|
}
|