constellation/api/attestationconfigapi/fetcher_test.go
2024-09-10 11:55:32 +02:00

189 lines
5.5 KiB
Go

/*
Copyright (c) Edgeless Systems GmbH
SPDX-License-Identifier: AGPL-3.0-only
*/
package attestationconfigapi
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"testing"
"time"
"github.com/edgelesssys/constellation/v2/internal/attestation/variant"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/stretchr/testify/assert"
)
func TestFetchLatestSEVSNPVersion(t *testing.T) {
latestVersionSNP := Entry{
SEVSNPVersion: SEVSNPVersion{
Microcode: 93,
TEE: 0,
SNP: 6,
Bootloader: 2,
},
}
olderVersionSNP := Entry{
SEVSNPVersion: SEVSNPVersion{
Microcode: 1,
TEE: 0,
SNP: 1,
Bootloader: 1,
},
}
latestVersionTDX := Entry{
TDXVersion: TDXVersion{
QESVN: 2,
PCESVN: 3,
TEETCBSVN: [16]byte{4},
QEVendorID: [16]byte{5},
XFAM: [8]byte{6},
},
}
olderVersionTDX := Entry{
TDXVersion: TDXVersion{
QESVN: 1,
PCESVN: 2,
TEETCBSVN: [16]byte{3},
QEVendorID: [16]byte{4},
XFAM: [8]byte{5},
},
}
latestStr := "2023-06-11-14-09.json"
olderStr := "2019-01-01-01-01.json"
testCases := map[string]struct {
fetcherVersions []string
timeAtTest time.Time
wantErr bool
attestation variant.Variant
expectedVersion Entry
olderVersion Entry
latestVersion Entry
}{
"get latest version azure-sev-snp": {
fetcherVersions: []string{latestStr, olderStr},
attestation: variant.AzureSEVSNP{},
expectedVersion: func() Entry { tmp := latestVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
olderVersion: func() Entry { tmp := olderVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
latestVersion: func() Entry { tmp := latestVersionSNP; tmp.Variant = variant.AzureSEVSNP{}; return tmp }(),
},
"get latest version aws-sev-snp": {
fetcherVersions: []string{latestStr, olderStr},
attestation: variant.AWSSEVSNP{},
expectedVersion: func() Entry { tmp := latestVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
olderVersion: func() Entry { tmp := olderVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
latestVersion: func() Entry { tmp := latestVersionSNP; tmp.Variant = variant.AWSSEVSNP{}; return tmp }(),
},
"get latest version azure-tdx": {
fetcherVersions: []string{latestStr, olderStr},
attestation: variant.AzureTDX{},
expectedVersion: func() Entry { tmp := latestVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
olderVersion: func() Entry { tmp := olderVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
latestVersion: func() Entry { tmp := latestVersionTDX; tmp.Variant = variant.AzureTDX{}; return tmp }(),
},
}
for name, tc := range testCases {
t.Run(name, func(t *testing.T) {
client := &http.Client{
Transport: &fakeConfigAPIHandler{
attestation: tc.attestation,
versions: tc.fetcherVersions,
latestDate: latestStr,
latestVersion: tc.latestVersion,
olderDate: olderStr,
olderVersion: tc.olderVersion,
},
}
fetcher := newFetcherWithClientAndVerifier(client, stubVerifier{}, constants.CDNRepositoryURL)
res, err := fetcher.FetchLatestVersion(context.Background(), tc.attestation)
assert := assert.New(t)
if tc.wantErr {
assert.Error(err)
} else {
assert.NoError(err)
assert.Equal(tc.expectedVersion, res)
}
})
}
}
type fakeConfigAPIHandler struct {
attestation variant.Variant
versions []string
latestDate string
latestVersion Entry
olderDate string
olderVersion Entry
}
// RoundTrip resolves the request and returns a dummy response.
func (f *fakeConfigAPIHandler) RoundTrip(req *http.Request) (*http.Response, error) {
switch req.URL.Path {
case fmt.Sprintf("/constellation/v1/attestation/%s/list", f.attestation.String()):
res := &http.Response{}
bt, err := json.Marshal(f.versions)
if err != nil {
return nil, err
}
res.Body = io.NopCloser(bytes.NewReader(bt))
res.Header = http.Header{}
res.Header.Set("Content-Type", "application/json")
res.StatusCode = http.StatusOK
return res, nil
case fmt.Sprintf("/constellation/v1/attestation/%s/list.sig", f.attestation.String()):
res := &http.Response{}
res.Body = io.NopCloser(bytes.NewReader([]byte("null")))
res.StatusCode = http.StatusOK
return res, nil
case fmt.Sprintf("/constellation/v1/attestation/%s/%s", f.attestation.String(), f.latestDate):
res := &http.Response{}
bt, err := json.Marshal(f.latestVersion)
if err != nil {
return nil, err
}
res.Body = io.NopCloser(bytes.NewReader(bt))
res.StatusCode = http.StatusOK
return res, nil
case fmt.Sprintf("/constellation/v1/attestation/%s/%s", f.attestation.String(), f.olderDate):
res := &http.Response{}
bt, err := json.Marshal(f.olderVersion)
if err != nil {
return nil, err
}
res.Body = io.NopCloser(bytes.NewReader(bt))
res.StatusCode = http.StatusOK
return res, nil
case fmt.Sprintf("/constellation/v1/attestation/%s/%s.sig", f.attestation.String(), f.latestDate):
res := &http.Response{}
res.Body = io.NopCloser(bytes.NewReader([]byte("null")))
res.StatusCode = http.StatusOK
return res, nil
case fmt.Sprintf("/constellation/v1/attestation/%s/%s.sig", f.attestation.String(), f.olderDate):
res := &http.Response{}
res.Body = io.NopCloser(bytes.NewReader([]byte("null")))
res.StatusCode = http.StatusOK
return res, nil
}
return nil, errors.New("no endpoint found")
}
type stubVerifier struct{}
func (s stubVerifier) VerifySignature(_, _ []byte) error {
return nil
}