2022-09-05 03:06:08 -04:00
/ *
Copyright ( c ) Edgeless Systems GmbH
SPDX - License - Identifier : AGPL - 3.0 - only
* /
2022-08-01 03:37:05 -04:00
package cmd
import (
"bytes"
2022-10-11 07:57:52 -04:00
"errors"
2022-11-28 04:27:33 -05:00
"fmt"
2022-08-05 09:30:23 -04:00
"io"
2022-08-01 03:37:05 -04:00
"net/http"
"net/url"
"testing"
2022-09-21 07:47:57 -04:00
"github.com/edgelesssys/constellation/v2/internal/cloud/cloudprovider"
"github.com/edgelesssys/constellation/v2/internal/config"
"github.com/edgelesssys/constellation/v2/internal/constants"
"github.com/edgelesssys/constellation/v2/internal/file"
2023-01-04 04:46:29 -05:00
"github.com/edgelesssys/constellation/v2/internal/logger"
2023-01-05 09:52:57 -05:00
"github.com/edgelesssys/constellation/v2/internal/versionsapi"
2022-08-01 03:37:05 -04:00
"github.com/spf13/afero"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func urlMustParse ( raw string ) * url . URL {
parsed , _ := url . Parse ( raw )
return parsed
}
func TestParseFetchMeasurementsFlags ( t * testing . T ) {
testCases := map [ string ] struct {
urlFlag string
signatureURLFlag string
configFlag string
2023-01-31 05:45:31 -05:00
forceFlag bool
2022-08-01 03:37:05 -04:00
wantFlags * fetchMeasurementsFlags
wantErr bool
} {
"default" : {
wantFlags : & fetchMeasurementsFlags {
measurementsURL : nil ,
signatureURL : nil ,
2022-11-15 09:40:49 -05:00
configPath : constants . ConfigFilename ,
2022-08-01 03:37:05 -04:00
} ,
} ,
"url" : {
urlFlag : "https://some.other.url/with/path" ,
signatureURLFlag : "https://some.other.url/with/path.sig" ,
wantFlags : & fetchMeasurementsFlags {
measurementsURL : urlMustParse ( "https://some.other.url/with/path" ) ,
signatureURL : urlMustParse ( "https://some.other.url/with/path.sig" ) ,
2022-11-15 09:40:49 -05:00
configPath : constants . ConfigFilename ,
2022-08-01 03:37:05 -04:00
} ,
} ,
"broken url" : {
urlFlag : "%notaurl%" ,
wantErr : true ,
} ,
"config" : {
configFlag : "someOtherConfig.yaml" ,
wantFlags : & fetchMeasurementsFlags {
2022-11-15 09:40:49 -05:00
configPath : "someOtherConfig.yaml" ,
2022-08-01 03:37:05 -04:00
} ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
require := require . New ( t )
cmd := newConfigFetchMeasurementsCmd ( )
2022-10-11 07:57:52 -04:00
cmd . Flags ( ) . String ( "config" , constants . ConfigFilename , "" ) // register persistent flag manually
2023-01-31 05:45:31 -05:00
cmd . Flags ( ) . Bool ( "force" , false , "" ) // register persistent flag manually
2022-08-01 03:37:05 -04:00
if tc . urlFlag != "" {
require . NoError ( cmd . Flags ( ) . Set ( "url" , tc . urlFlag ) )
}
if tc . signatureURLFlag != "" {
require . NoError ( cmd . Flags ( ) . Set ( "signature-url" , tc . signatureURLFlag ) )
}
if tc . configFlag != "" {
require . NoError ( cmd . Flags ( ) . Set ( "config" , tc . configFlag ) )
}
2023-01-04 04:46:29 -05:00
cfm := & configFetchMeasurementsCmd { log : logger . NewTest ( t ) }
flags , err := cfm . parseFetchMeasurementsFlags ( cmd )
2022-08-01 03:37:05 -04:00
if tc . wantErr {
assert . Error ( err )
return
}
require . NoError ( err )
assert . Equal ( tc . wantFlags , flags )
} )
}
}
func TestUpdateURLs ( t * testing . T ) {
2023-01-05 09:52:57 -05:00
ver := versionsapi . Version {
Ref : "foo" ,
Stream : "nightly" ,
Version : "v7.7.7" ,
Kind : versionsapi . VersionKindImage ,
}
2022-08-01 03:37:05 -04:00
testCases := map [ string ] struct {
conf * config . Config
flags * fetchMeasurementsFlags
wantMeasurementsURL string
wantMeasurementsSigURL string
} {
"both values nil" : {
conf : & config . Config {
2023-01-05 09:52:57 -05:00
Image : ver . ShortPath ( ) ,
2022-08-01 03:37:05 -04:00
Provider : config . ProviderConfig {
2022-11-22 12:47:08 -05:00
GCP : & config . GCPConfig { } ,
2022-08-01 03:37:05 -04:00
} ,
} ,
flags : & fetchMeasurementsFlags { } ,
2023-05-25 08:33:57 -04:00
wantMeasurementsURL : ver . ArtifactsURL ( versionsapi . APIV2 ) + "/image/measurements.json" ,
wantMeasurementsSigURL : ver . ArtifactsURL ( versionsapi . APIV2 ) + "/image/measurements.json.sig" ,
2022-08-01 03:37:05 -04:00
} ,
"both set by user" : {
2023-02-03 05:05:42 -05:00
conf : & config . Config {
Image : ver . ShortPath ( ) ,
} ,
2022-08-01 03:37:05 -04:00
flags : & fetchMeasurementsFlags {
2022-11-24 04:57:58 -05:00
measurementsURL : urlMustParse ( "get.my/measurements.json" ) ,
signatureURL : urlMustParse ( "get.my/measurements.json.sig" ) ,
2022-08-01 03:37:05 -04:00
} ,
2022-11-24 04:57:58 -05:00
wantMeasurementsURL : "get.my/measurements.json" ,
wantMeasurementsSigURL : "get.my/measurements.json.sig" ,
2022-08-01 03:37:05 -04:00
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
2022-11-28 04:27:33 -05:00
err := tc . flags . updateURLs ( tc . conf )
2022-08-01 03:37:05 -04:00
assert . NoError ( err )
assert . Equal ( tc . wantMeasurementsURL , tc . flags . measurementsURL . String ( ) )
} )
}
}
// roundTripFunc .
type roundTripFunc func ( req * http . Request ) * http . Response
// RoundTrip .
func ( f roundTripFunc ) RoundTrip ( req * http . Request ) ( * http . Response , error ) {
return f ( req ) , nil
}
// newTestClient returns *http.Client with Transport replaced to avoid making real calls.
func newTestClient ( fn roundTripFunc ) * http . Client {
return & http . Client {
Transport : fn ,
}
}
func TestConfigFetchMeasurements ( t * testing . T ) {
2022-11-28 04:27:33 -05:00
// Cosign private key used to sign the measurements.
// Generated with: cosign generate-key-pair
// Password left empty.
//
// -----BEGIN ENCRYPTED COSIGN PRIVATE KEY-----
// eyJrZGYiOnsibmFtZSI6InNjcnlwdCIsInBhcmFtcyI6eyJOIjozMjc2OCwiciI6
// OCwicCI6MX0sInNhbHQiOiJlRHVYMWRQMGtIWVRnK0xkbjcxM0tjbFVJaU92eFVX
// VXgvNi9BbitFVk5BPSJ9LCJjaXBoZXIiOnsibmFtZSI6Im5hY2wvc2VjcmV0Ym94
// Iiwibm9uY2UiOiJwaWhLL2txNmFXa2hqSVVHR3RVUzhTVkdHTDNIWWp4TCJ9LCJj
// aXBoZXJ0ZXh0Ijoidm81SHVWRVFWcUZ2WFlQTTVPaTVaWHM5a255bndZU2dvcyth
// VklIeHcrOGFPamNZNEtvVjVmL3lHRHR0K3BHV2toanJPR1FLOWdBbmtsazFpQ0c5
// a2czUXpPQTZsU2JRaHgvZlowRVRZQ0hLeElncEdPRVRyTDlDenZDemhPZXVSOXJ6
// TDcvRjBBVy9vUDVqZXR3dmJMNmQxOEhjck9kWE8yVmYxY2w0YzNLZjVRcnFSZzlN
// dlRxQWFsNXJCNHNpY1JaMVhpUUJjb0YwNHc9PSJ9
// -----END ENCRYPTED COSIGN PRIVATE KEY-----
cosignPublicKey := [ ] byte ( "-----BEGIN PUBLIC KEY-----\nMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEu78QgxOOcao6U91CSzEXxrKhvFTt\nJHNy+eX6EMePtDm8CnDF9HSwnTlD0itGJ/XHPQA5YX10fJAqI1y+ehlFMw==\n-----END PUBLIC KEY-----" )
measurements := ` {
2023-05-22 08:59:28 -04:00
"version" : "v999.999.999" ,
"ref" : "-" ,
"stream" : "stable" ,
"list" : [
{
"csp" : "GCP" ,
"attestationVariant" : "gcp-sev-es" ,
"measurements" : {
"0" : {
"expected" : "0000000000000000000000000000000000000000000000000000000000000000" ,
"warnOnly" : false
} ,
"1" : {
"expected" : "1111111111111111111111111111111111111111111111111111111111111111" ,
"warnOnly" : false
} ,
"2" : {
"expected" : "2222222222222222222222222222222222222222222222222222222222222222" ,
"warnOnly" : false
} ,
"3" : {
"expected" : "3333333333333333333333333333333333333333333333333333333333333333" ,
"warnOnly" : false
} ,
"4" : {
"expected" : "4444444444444444444444444444444444444444444444444444444444444444" ,
"warnOnly" : false
} ,
"5" : {
"expected" : "5555555555555555555555555555555555555555555555555555555555555555" ,
"warnOnly" : false
} ,
"6" : {
"expected" : "6666666666666666666666666666666666666666666666666666666666666666" ,
"warnOnly" : false
}
}
}
]
2022-11-28 04:27:33 -05:00
}
2022-08-01 03:37:05 -04:00
`
2023-05-22 08:59:28 -04:00
signature := "MEUCIHQETkvMRy8WaWMroX4Aa2J86bTW0kGMp8NG0YLXJKZJAiEA7ZdxoQzSTyBFNhZ1bwB5eT3av0biAdb66dJRFxQlKLA="
2022-08-01 03:37:05 -04:00
client := newTestClient ( func ( req * http . Request ) * http . Response {
2023-05-22 08:59:28 -04:00
if req . URL . Path == "/constellation/v2/ref/-/stream/stable/v999.999.999/image/measurements.json" {
2022-08-01 03:37:05 -04:00
return & http . Response {
StatusCode : http . StatusOK ,
2022-08-05 09:30:23 -04:00
Body : io . NopCloser ( bytes . NewBufferString ( measurements ) ) ,
2022-08-01 03:37:05 -04:00
Header : make ( http . Header ) ,
}
}
2023-05-22 08:59:28 -04:00
if req . URL . Path == "/constellation/v2/ref/-/stream/stable/v999.999.999/image/measurements.json.sig" {
2022-08-01 03:37:05 -04:00
return & http . Response {
StatusCode : http . StatusOK ,
2022-08-05 09:30:23 -04:00
Body : io . NopCloser ( bytes . NewBufferString ( signature ) ) ,
2022-08-01 03:37:05 -04:00
Header : make ( http . Header ) ,
}
}
2022-11-28 04:27:33 -05:00
fmt . Println ( "unexpected request" , req . URL . String ( ) )
2022-08-01 03:37:05 -04:00
return & http . Response {
StatusCode : http . StatusNotFound ,
2022-08-05 09:30:23 -04:00
Body : io . NopCloser ( bytes . NewBufferString ( "Not found." ) ) ,
2022-08-01 03:37:05 -04:00
Header : make ( http . Header ) ,
}
} )
2022-10-11 07:57:52 -04:00
testCases := map [ string ] struct {
verifier rekorVerifier
} {
"success" : {
verifier : singleUUIDVerifier ( ) ,
} ,
"failing search should not result in error" : {
verifier : & stubRekorVerifier {
SearchByHashUUIDs : [ ] string { } ,
SearchByHashError : errors . New ( "some error" ) ,
} ,
} ,
"failing verify should not result in error" : {
verifier : & stubRekorVerifier {
SearchByHashUUIDs : [ ] string { "11111111111111111111111111111111111111111111111111111111111111111111111111111111" } ,
VerifyEntryError : errors . New ( "some error" ) ,
} ,
} ,
}
for name , tc := range testCases {
t . Run ( name , func ( t * testing . T ) {
assert := assert . New ( t )
require := require . New ( t )
cmd := newConfigFetchMeasurementsCmd ( )
cmd . Flags ( ) . String ( "config" , constants . ConfigFilename , "" ) // register persistent flag manually
2023-01-31 05:45:31 -05:00
cmd . Flags ( ) . Bool ( "force" , true , "" ) // register persistent flag manually
2022-10-11 07:57:52 -04:00
fileHandler := file . NewHandler ( afero . NewMemMapFs ( ) )
2022-11-15 09:40:49 -05:00
gcpConfig := defaultConfigWithExpectedMeasurements ( t , config . Default ( ) , cloudprovider . GCP )
2022-11-28 04:27:33 -05:00
gcpConfig . Image = "v999.999.999"
2022-10-11 07:57:52 -04:00
err := fileHandler . WriteYAML ( constants . ConfigFilename , gcpConfig , file . OptMkdirAll )
require . NoError ( err )
2023-01-04 04:46:29 -05:00
cfm := & configFetchMeasurementsCmd { log : logger . NewTest ( t ) }
2022-10-11 07:57:52 -04:00
2023-01-04 04:46:29 -05:00
assert . NoError ( cfm . configFetchMeasurements ( cmd , tc . verifier , cosignPublicKey , fileHandler , client ) )
2022-10-11 07:57:52 -04:00
} )
}
2022-08-01 03:37:05 -04:00
}