94 lines
3.2 KiB
Go
94 lines
3.2 KiB
Go
|
|
package artifact
|
||
|
|
|
||
|
|
import (
|
||
|
|
"crypto/sha256"
|
||
|
|
"encoding/hex"
|
||
|
|
"os"
|
||
|
|
"path/filepath"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestBuildManifestSortsNamesAndOmitsEmptySourceOnlyManifest(t *testing.T) {
|
||
|
|
manifest, err := BuildManifest([]ExpectedAsset{
|
||
|
|
assetFor("zeta.bin", []byte("zeta")),
|
||
|
|
assetFor("alpha.bin", []byte("alpha")),
|
||
|
|
})
|
||
|
|
if err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
want := assetDigest([]byte("alpha")) + " alpha.bin\n" + assetDigest([]byte("zeta")) + " zeta.bin\n"
|
||
|
|
if string(manifest.Content) != want || manifest.Filename != ManifestFilename || manifest.SHA256 != assetDigest([]byte(want)) || manifest.Size != int64(len(want)) {
|
||
|
|
t.Fatalf("manifest = %#v, want content %q", manifest, want)
|
||
|
|
}
|
||
|
|
empty, err := BuildManifest(nil)
|
||
|
|
if err != nil || empty.Filename != "" || len(empty.Content) != 0 {
|
||
|
|
t.Fatalf("empty manifest = %#v, error = %v", empty, err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestBuildManifestRejectsInvalidOrDuplicateDeclarations(t *testing.T) {
|
||
|
|
for _, test := range []struct {
|
||
|
|
name string
|
||
|
|
assets []ExpectedAsset
|
||
|
|
}{
|
||
|
|
{name: "invalid filename", assets: []ExpectedAsset{assetFor("../app.bin", []byte("app"))}},
|
||
|
|
{name: "manifest filename", assets: []ExpectedAsset{assetFor(ManifestFilename, []byte("app"))}},
|
||
|
|
{name: "invalid digest", assets: []ExpectedAsset{{Filename: "app.bin", SHA256: "not-a-digest"}}},
|
||
|
|
{name: "duplicate filename", assets: []ExpectedAsset{assetFor("app.bin", []byte("one")), assetFor("app.bin", []byte("two"))}},
|
||
|
|
} {
|
||
|
|
t.Run(test.name, func(t *testing.T) {
|
||
|
|
if _, err := BuildManifest(test.assets); err == nil {
|
||
|
|
t.Fatal("BuildManifest() succeeded for invalid declarations")
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestVerifyLocalAssetsRejectsSameSizeTampering(t *testing.T) {
|
||
|
|
path := filepath.Join(t.TempDir(), "app.bin")
|
||
|
|
original := []byte("alpha")
|
||
|
|
if err := os.WriteFile(path, original, 0o600); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
asset := assetFor(filepath.Base(path), original)
|
||
|
|
asset.Content = nil
|
||
|
|
asset.Path = path
|
||
|
|
if err := VerifyLocalAssets([]ExpectedAsset{asset}); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
|
||
|
|
tampered := []byte("bravo")
|
||
|
|
if err := os.WriteFile(path, tampered, 0o600); err != nil {
|
||
|
|
t.Fatal(err)
|
||
|
|
}
|
||
|
|
if err := VerifyLocalAssets([]ExpectedAsset{asset}); err == nil || !strings.Contains(err.Error(), "does not match declared asset") {
|
||
|
|
t.Fatalf("VerifyLocalAssets() error = %v, want content mismatch", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func TestValidateExpectedAssetsRejectsDuplicateAndMismatchedInlineContent(t *testing.T) {
|
||
|
|
asset := assetFor("app.bin", []byte("alpha"))
|
||
|
|
if err := ValidateExpectedAssets([]ExpectedAsset{asset, asset}); err == nil || !strings.Contains(err.Error(), "duplicate") {
|
||
|
|
t.Fatalf("ValidateExpectedAssets() error = %v, want duplicate", err)
|
||
|
|
}
|
||
|
|
asset.Content = []byte("bravo")
|
||
|
|
if err := ValidateExpectedAssets([]ExpectedAsset{asset}); err == nil || !strings.Contains(err.Error(), "does not match identity") {
|
||
|
|
t.Fatalf("ValidateExpectedAssets() error = %v, want content mismatch", err)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func assetFor(filename string, content []byte) ExpectedAsset {
|
||
|
|
return ExpectedAsset{
|
||
|
|
Filename: filename,
|
||
|
|
SHA256: assetDigest(content),
|
||
|
|
Size: int64(len(content)),
|
||
|
|
Content: append([]byte(nil), content...),
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
func assetDigest(content []byte) string {
|
||
|
|
sum := sha256.Sum256(content)
|
||
|
|
return hex.EncodeToString(sum[:])
|
||
|
|
}
|