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"
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
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
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 ) )
}
flags , err := parseFetchMeasurementsFlags ( cmd )
if tc . wantErr {
assert . Error ( err )
return
}
require . NoError ( err )
assert . Equal ( tc . wantFlags , flags )
} )
}
}
func TestUpdateURLs ( t * testing . T ) {
testCases := map [ string ] struct {
conf * config . Config
flags * fetchMeasurementsFlags
wantMeasurementsURL string
wantMeasurementsSigURL string
} {
"both values nil" : {
conf : & config . Config {
2022-11-22 12:47:08 -05:00
Image : "someImageVersion" ,
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 { } ,
2022-11-28 04:27:33 -05:00
wantMeasurementsURL : constants . CDNRepositoryURL + "/" + constants . CDNMeasurementsPath + "/someImageVersion/gcp/measurements.json" ,
wantMeasurementsSigURL : constants . CDNRepositoryURL + "/" + constants . CDNMeasurementsPath + "/someImageVersion/gcp/measurements.json.sig" ,
2022-08-01 03:37:05 -04:00
} ,
"both set by user" : {
conf : & config . Config { } ,
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 := ` {
"csp" : "gcp" ,
"image" : "v999.999.999" ,
"measurements" : {
"0" : "0000000000000000000000000000000000000000000000000000000000000000" ,
"1" : "1111111111111111111111111111111111111111111111111111111111111111" ,
"2" : "2222222222222222222222222222222222222222222222222222222222222222" ,
"3" : "3333333333333333333333333333333333333333333333333333333333333333" ,
"4" : "4444444444444444444444444444444444444444444444444444444444444444" ,
"5" : "5555555555555555555555555555555555555555555555555555555555555555" ,
"6" : "6666666666666666666666666666666666666666666666666666666666666666"
}
}
2022-08-01 03:37:05 -04:00
`
2022-11-28 04:27:33 -05:00
signature := "MEYCIQDRAQNK2NjHJBGrnw3HQAyBsXMCmVCptBdgA6VZ3IlyiAIhAPG42waF1aFZq7dnjP3b2jsMNUtaKYDQQSazW1AX8jgF"
2022-08-01 03:37:05 -04:00
client := newTestClient ( func ( req * http . Request ) * http . Response {
2022-11-28 04:27:33 -05:00
if req . URL . String ( ) == "https://cdn.confidential.cloud/constellation/v1/measurements/v999.999.999/gcp/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 ) ,
}
}
2022-11-28 04:27:33 -05:00
if req . URL . String ( ) == "https://cdn.confidential.cloud/constellation/v1/measurements/v999.999.999/gcp/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
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 )
2022-11-28 04:27:33 -05:00
assert . NoError ( configFetchMeasurements ( cmd , tc . verifier , cosignPublicKey , fileHandler , client ) )
2022-10-11 07:57:52 -04:00
} )
}
2022-08-01 03:37:05 -04:00
}