80 lines
2.5 KiB
Go
80 lines
2.5 KiB
Go
package bundle
|
|
|
|
import (
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
)
|
|
|
|
func ValidateManifest(manifest Manifest) error {
|
|
if manifest.SchemaVersion != SchemaVersion {
|
|
return fmt.Errorf("schema_version must be %d", SchemaVersion)
|
|
}
|
|
if manifest.ID == "" {
|
|
return fmt.Errorf("id is required")
|
|
}
|
|
if err := ValidateDigest(manifest.Digest); err != nil {
|
|
return fmt.Errorf("digest: %w", err)
|
|
}
|
|
if manifest.Created.IsZero() {
|
|
return fmt.Errorf("created is required")
|
|
}
|
|
if len(manifest.Files) == 0 {
|
|
return fmt.Errorf("files is required")
|
|
}
|
|
seen := make(map[string]struct{}, len(manifest.Files))
|
|
for index, file := range manifest.Files {
|
|
if err := ValidateSourcePath(file.Path); err != nil {
|
|
return fmt.Errorf("files[%d].path: %w", index, err)
|
|
}
|
|
if err := ValidateDigest(file.SHA256); err != nil {
|
|
return fmt.Errorf("files[%d].sha256: %w", index, err)
|
|
}
|
|
if file.Size < 0 {
|
|
return fmt.Errorf("files[%d].size must be non-negative", index)
|
|
}
|
|
if _, exists := seen[file.Path]; exists {
|
|
return fmt.Errorf("files[%d].path duplicates %q", index, file.Path)
|
|
}
|
|
seen[file.Path] = struct{}{}
|
|
}
|
|
if actual := BundleDigest(manifest.Files); actual != manifest.Digest {
|
|
return fmt.Errorf("digest mismatch: got %s want %s", actual, manifest.Digest)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func ValidateBundle(root string, manifest Manifest) error {
|
|
if err := ValidateManifest(manifest); err != nil {
|
|
return err
|
|
}
|
|
files := append([]ManifestFile(nil), manifest.Files...)
|
|
for index, manifestFile := range files {
|
|
fullPath := filepath.Join(root, filepath.FromSlash(manifestFile.Path))
|
|
info, err := os.Lstat(fullPath)
|
|
if err != nil {
|
|
return fmt.Errorf("file %q stat: %w", manifestFile.Path, err)
|
|
}
|
|
if !info.Mode().IsRegular() {
|
|
return fmt.Errorf("file %q must be a regular file", manifestFile.Path)
|
|
}
|
|
if info.Size() != manifestFile.Size {
|
|
return fmt.Errorf("file %q size mismatch: got %d want %d", manifestFile.Path, info.Size(), manifestFile.Size)
|
|
}
|
|
data, err := os.ReadFile(fullPath)
|
|
if err != nil {
|
|
return fmt.Errorf("file %q read: %w", manifestFile.Path, err)
|
|
}
|
|
actualDigest := FileDigest(data)
|
|
if actualDigest != manifestFile.SHA256 {
|
|
return fmt.Errorf("file %q sha256 mismatch: got %s want %s", manifestFile.Path, actualDigest, manifestFile.SHA256)
|
|
}
|
|
files[index].SHA256 = actualDigest
|
|
files[index].Size = int64(len(data))
|
|
}
|
|
if actualDigest := BundleDigest(files); actualDigest != manifest.Digest {
|
|
return fmt.Errorf("digest mismatch: got %s want %s", actualDigest, manifest.Digest)
|
|
}
|
|
return nil
|
|
}
|