add unit test
This commit is contained in:
parent
ab49b2abbc
commit
71c2361dfc
11 changed files with 421 additions and 13 deletions
1
go.mod
1
go.mod
|
|
@ -58,6 +58,7 @@ require (
|
|||
google.golang.org/protobuf v1.36.6
|
||||
gopkg.in/h2non/gock.v1 v1.1.2
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
gotest.tools/v3 v3.0.3
|
||||
)
|
||||
|
||||
require (
|
||||
|
|
|
|||
6
go.sum
6
go.sum
|
|
@ -243,6 +243,7 @@ github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM=
|
|||
github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/certificate-transparency-go v1.3.1 h1:akbcTfQg0iZlANZLn0L9xOeWtyCIdeoYhKrqi5iH3Go=
|
||||
github.com/google/certificate-transparency-go v1.3.1/go.mod h1:gg+UQlx6caKEDQ9EElFOujyxEQEfOiQzAt6782Bvi8k=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI=
|
||||
|
|
@ -410,6 +411,7 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH
|
|||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=
|
||||
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
|
@ -483,6 +485,7 @@ github.com/spf13/cast v1.7.1 h1:cuNEagBQEHWN1FnbGEjCXL2szYEXqfJPbP2HNUaca9Y=
|
|||
github.com/spf13/cast v1.7.1/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cAdo=
|
||||
github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo=
|
||||
github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4=
|
||||
|
|
@ -560,6 +563,7 @@ golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCR
|
|||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
|
|
@ -597,11 +601,13 @@ golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
|||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190624222133-a101b041ded4/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.29.0 h1:Xx0h3TtM9rzQpQuR4dKLrdglAmCEN5Oi+P74JdhdzXE=
|
||||
golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.227.0 h1:QvIHF9IuyG6d6ReE+BNd11kIB8hZvjN8Z5xY5t21zYc=
|
||||
google.golang.org/api v0.227.0/go.mod h1:EIpaG6MbTgQarWF5xJvX0eOJPK9n/5D4Bynb9j2HXvQ=
|
||||
google.golang.org/genproto v0.0.0-20250303144028-a0af3efb3deb h1:ITgPrl429bc6+2ZraNSzMDk3I95nmQln2fuPstKwFDE=
|
||||
|
|
|
|||
|
|
@ -6,6 +6,13 @@ import (
|
|||
"github.com/cli/cli/v2/pkg/cmd/attestation/test/data"
|
||||
)
|
||||
|
||||
func makeTestReleaseAttestation() Attestation {
|
||||
return Attestation{
|
||||
Bundle: data.GitHubReleaseBundle(nil),
|
||||
BundleURL: "https://example.com",
|
||||
}
|
||||
}
|
||||
|
||||
func makeTestAttestation() Attestation {
|
||||
return Attestation{Bundle: data.SigstoreBundle(nil), BundleURL: "https://example.com"}
|
||||
}
|
||||
|
|
@ -26,8 +33,12 @@ func (m MockClient) GetTrustDomain() (string, error) {
|
|||
func OnGetByDigestSuccess(params FetchParams) ([]*Attestation, error) {
|
||||
att1 := makeTestAttestation()
|
||||
att2 := makeTestAttestation()
|
||||
att3 := makeTestReleaseAttestation()
|
||||
attestations := []*Attestation{&att1, &att2}
|
||||
if params.PredicateType != "" {
|
||||
if params.PredicateType == "https://in-toto.io/attestation/release/v0.1" {
|
||||
attestations = append(attestations, &att3)
|
||||
}
|
||||
return FilterAttestations(params.PredicateType, attestations)
|
||||
}
|
||||
|
||||
|
|
|
|||
28
pkg/cmd/attestation/test/data/a.zip
Normal file
28
pkg/cmd/attestation/test/data/a.zip
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
a # frozen_string_literal: true
|
||||
|
||||
source "https://rubygems.org"
|
||||
|
||||
source "https://rubygems.pkg.github.com/github" do
|
||||
gem "entitlements-aad-plugin", "~> 1.0"
|
||||
gem "entitlements-app", "~> 1.2"
|
||||
gem "entitlements-github-plugin", "~> 1.2"
|
||||
gem "entitlements-gitrepo-auditor-plugin", "~> 1.0"
|
||||
gem "entitlements-jit-github-plugin", "~> 1.0"
|
||||
gem "entitlements-lib", "~> 0.2"
|
||||
gem "entitlements-stafftools-plugin", "~> 1.0"
|
||||
end
|
||||
|
||||
group :development do
|
||||
gem "base64", "~> 0.2.0"
|
||||
gem "irb", "~> 1.15"
|
||||
gem "pry", "~> 0.14"
|
||||
gem "pry-byebug", "~> 3.9"
|
||||
gem "pry-rescue", "~> 1.6"
|
||||
gem "rspec", "~> 3.13"
|
||||
gem "rubocop", "~> 1.71"
|
||||
gem "rubocop-github", "~> 0.20.0"
|
||||
gem "rubocop-performance"
|
||||
gem "rubocop-rspec", "~> 3.4.0"
|
||||
gem "simplecov", "~> 0.21"
|
||||
gem "simplecov-erb", "~> 1.0.0"
|
||||
end
|
||||
|
|
@ -10,6 +10,9 @@ import (
|
|||
//go:embed sigstore-js-2.1.0-bundle.json
|
||||
var SigstoreBundleRaw []byte
|
||||
|
||||
//go:embed github_release_bundle.json
|
||||
var GitHubReleaseBundleRaw []byte
|
||||
|
||||
// SigstoreBundle returns a test sigstore-go bundle.Bundle
|
||||
func SigstoreBundle(t *testing.T) *bundle.Bundle {
|
||||
b := &bundle.Bundle{}
|
||||
|
|
@ -19,3 +22,12 @@ func SigstoreBundle(t *testing.T) *bundle.Bundle {
|
|||
}
|
||||
return b
|
||||
}
|
||||
|
||||
func GitHubReleaseBundle(t *testing.T) *bundle.Bundle {
|
||||
b := &bundle.Bundle{}
|
||||
err := b.UnmarshalJSON(GitHubReleaseBundleRaw)
|
||||
if err != nil {
|
||||
t.Fatalf("failed to unmarshal GitHub release bundle: %v", err)
|
||||
}
|
||||
return b
|
||||
}
|
||||
|
|
|
|||
24
pkg/cmd/attestation/test/data/github_release_bundle.json
Normal file
24
pkg/cmd/attestation/test/data/github_release_bundle.json
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
{
|
||||
"mediaType": "application/vnd.dev.sigstore.bundle.v0.3+json",
|
||||
"verificationMaterial": {
|
||||
"timestampVerificationData": {
|
||||
"rfc3161Timestamps": [
|
||||
{
|
||||
"signedTimestamp": "MIIC0TADAgEAMIICyAYJKoZIhvcNAQcCoIICuTCCArUCAQMxDTALBglghkgBZQMEAgIwgbwGCyqGSIb3DQEJEAEEoIGsBIGpMIGmAgEBBgkrBgEEAYO/MAIwMTANBglghkgBZQMEAgEFAAQgGvFc6nUuLhnXfhM9p0DV91c5kHvafP1hs9BX8KYeeSYCFQDhjGrIIiaH/jkMdN6HUsErnUfrlRgPMjAyNTA1MTMyMzAzNTFaMAMCAQGgNqQ0MDIxFTATBgNVBAoTDEdpdEh1YiwgSW5jLjEZMBcGA1UEAxMQVFNBIFRpbWVzdGFtcGluZ6AAMYIB3jCCAdoCAQEwSjAyMRUwEwYDVQQKEwxHaXRIdWIsIEluYy4xGTAXBgNVBAMTEFRTQSBpbnRlcm1lZGlhdGUCFB+7MIjE5/rL4XA4fNDnmXHA04+wMAsGCWCGSAFlAwQCAqCCAQUwGgYJKoZIhvcNAQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yNTA1MTMyMzAzNTFaMD8GCSqGSIb3DQEJBDEyBDDVh2oDCJy7ustugLKfVcUSNjo5M2MFMNKIU11sIQDCNOo5gbj9R97sCWXNnfmUztMwgYcGCyqGSIb3DQEJEAIvMXgwdjB0MHIEIHuISsKSyiJtlhGjT+RyS+tYQ7iwCMsMCTGmz2NK3D7DME4wNqQ0MDIxFTATBgNVBAoTDEdpdEh1YiwgSW5jLjEZMBcGA1UEAxMQVFNBIGludGVybWVkaWF0ZQIUH7swiMTn+svhcDh80OeZccDTj7AwCgYIKoZIzj0EAwMEZzBlAjAqp/fYVfQcU9aMcmTIZvb0cxk00OaVBYLzuiIvcRqkMdAJiz/gSxOWU0AQjEPskHUCMQCrUKlZR4shPZuMvY6CCUOhxxKq/6LUoccWNHyL6sGkHRXE7j9HETh4uLKzRwNDVVA="
|
||||
}
|
||||
]
|
||||
},
|
||||
"certificate": {
|
||||
"rawBytes": "MIICKjCCAbCgAwIBAgIUaa62dj98DUB+TpyvKtVaR4vGSM0wCgYIKoZIzj0EAwMwODEVMBMGA1UEChMMR2l0SHViLCBJbmMuMR8wHQYDVQQDExZGdWxjaW8gSW50ZXJtZWRpYXRlIGwxMB4XDTI1MDMxMDE1MDMwMloXDTI2MDMxMDE1MDMwMlowKjEVMBMGA1UEChMMR2l0SHViLCBJbmMuMREwDwYDVQQDEwhBdHRlc3RlcjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABIMB7plPnZvBRlC2lvAocKTAqAPMJqstEqYk26e9vDJDC1yqoiHxZfPV4W/1RqUMZD1dFKm9t4RiSmm73/QnQKajgaUwgaIwDgYDVR0PAQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMAwGA1UdEwEB/wQCMAAwHQYDVR0OBBYEFOqaGpr5SbdYk5CQXsmmDZCBHR+XMB8GA1UdIwQYMBaAFMDhuFKkS08+3no4EQbPSY6hRZszMC0GA1UdEQQmMCSGImh0dHBzOi8vZG90Y29tLnJlbGVhc2VzLmdpdGh1Yi5jb20wCgYIKoZIzj0EAwMDaAAwZQIwWFdF6xcXazHVPHEAtd1SeaizLdY1erRl5hK+XlwhfpnasQHHZ9bdu4Zj8ARhW/AhAjEArujhmJGo7Fi4/Ek1RN8bufs6UhIQneQd/pxE8QdorwZkj2C8nf2EzrUYzlxKfktC"
|
||||
}
|
||||
},
|
||||
"dsseEnvelope": {
|
||||
"payload": "eyJfdHlwZSI6Imh0dHBzOi8vaW4tdG90by5pby9TdGF0ZW1lbnQvdjEiLCJzdWJqZWN0IjpbeyJ1cmkiOiJwa2c6Z2l0aHViL2JkZWhhbWVyL2RlbG1lQHY1IiwiZGlnZXN0Ijp7InNoYTEiOiJjNWUxN2E2MmUwNmExZDIwMTU3MDI0OWM2MWZhZTUzMWU5MjQ0ZTFiIn19LHsibmFtZSI6ImEuemlwIiwiZGlnZXN0Ijp7InNoYTI1NiI6ImY3MTY1ODQ4ZjlmNWRkYzU3OGQ3YWRiZDFmNTY2YTM5NDE2OTM4NWM3M2JkODhiZjYwZGY3ZTc1OWRiOGUwOGQifX0seyJuYW1lIjoiYi56aXAiLCJkaWdlc3QiOnsic2hhMjU2IjoiOGI3ZWIxNTcyMzQ2NjkyZmZkM2FlMDEyNDhjNzBhMzQxYWUzYWE4YmUxZGY4YjEyMzQ2YjUwYWNiOTAwMjI4MiJ9fV0sInByZWRpY2F0ZVR5cGUiOiJodHRwczovL2luLXRvdG8uaW8vYXR0ZXN0YXRpb24vcmVsZWFzZS92MC4xIiwicHJlZGljYXRlIjp7Im93bmVySWQiOiIzOTgwMjciLCJwdXJsIjoicGtnOmdpdGh1Yi9iZGVoYW1lci9kZWxtZUB2NSIsInJlbGVhc2VJZCI6IjIxODQxOTIxNyIsInJlcG9zaXRvcnkiOiJiZGVoYW1lci9kZWxtZSIsInJlcG9zaXRvcnlJZCI6IjkwNTk4ODA0NCIsInRhZyI6InY1In19",
|
||||
"payloadType": "application/vnd.in-toto+json",
|
||||
"signatures": [
|
||||
{
|
||||
"sig": "MEQCIH6LDUanQYOCPovZlIqI1cE49SiGJdexR65qsAZHohsZAiA9w3usgPWtgn5voB8bRvpJQtjEVqC5eMDh3mJEdyMcXw=="
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
|
@ -281,3 +281,11 @@ func StubFetchRelease(t *testing.T, reg *httpmock.Registry, owner, repoName, tag
|
|||
)
|
||||
}
|
||||
}
|
||||
|
||||
func StubFetchRefSHA(t *testing.T, reg *httpmock.Registry, owner, repoName, tagName, sha string) {
|
||||
path := fmt.Sprintf("repos/%s/%s/git/refs/tags/%s", owner, repoName, tagName)
|
||||
reg.Register(
|
||||
httpmock.REST("GET", path),
|
||||
httpmock.StringResponse(fmt.Sprintf(`{"object": {"sha": "%s"}}`, sha)),
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -27,14 +27,17 @@ func NewCmdVerifyAsset(f *cmdutil.Factory, runF func(*attestation.AttestOptions)
|
|||
Use: "verify-asset <tag> <file-path>",
|
||||
Short: "Verify that a given asset originated from a specific GitHub Release.",
|
||||
Hidden: true,
|
||||
Args: cobra.ExactArgs(2),
|
||||
Args: cobra.MaximumNArgs(2),
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 2 {
|
||||
return cmdutil.FlagErrorf("You must specify a tag and a file path")
|
||||
}
|
||||
|
||||
tagName := args[0]
|
||||
assetFilePath := args[1]
|
||||
if len(args) == 2 {
|
||||
opts.TagName = args[0]
|
||||
opts.AssetFilePath = args[1]
|
||||
} else if len(args) == 1 {
|
||||
opts.AssetFilePath = args[0]
|
||||
} else {
|
||||
return cmdutil.FlagErrorf("you must specify an asset filepath")
|
||||
}
|
||||
|
||||
httpClient, err := f.HttpClient()
|
||||
if err != nil {
|
||||
|
|
@ -53,8 +56,8 @@ func NewCmdVerifyAsset(f *cmdutil.Factory, runF func(*attestation.AttestOptions)
|
|||
}
|
||||
|
||||
*opts = attestation.AttestOptions{
|
||||
TagName: tagName,
|
||||
AssetFilePath: assetFilePath,
|
||||
TagName: opts.TagName,
|
||||
AssetFilePath: opts.AssetFilePath,
|
||||
Repo: baseRepo.RepoOwner() + "/" + baseRepo.RepoName(),
|
||||
APIClient: api.NewLiveClient(httpClient, hostname, logger),
|
||||
Limit: 10,
|
||||
|
|
@ -114,6 +117,15 @@ func NewCmdVerifyAsset(f *cmdutil.Factory, runF func(*attestation.AttestOptions)
|
|||
|
||||
func verifyAssetRun(opts *attestation.AttestOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
if opts.TagName == "" {
|
||||
release, err := shared.FetchLatestRelease(ctx, opts.HttpClient, opts.BaseRepo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.TagName = release.TagName
|
||||
}
|
||||
|
||||
fileName := getFileName(opts.AssetFilePath)
|
||||
|
||||
// calculate the digest of the file
|
||||
|
|
|
|||
158
pkg/cmd/release/verify-asset/verify-asset_test.go
Normal file
158
pkg/cmd/release/verify-asset/verify-asset_test.go
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
package verifyasset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/api"
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/io"
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/verification"
|
||||
"github.com/cli/cli/v2/pkg/cmd/release/attestation"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
|
||||
"github.com/cli/cli/v2/pkg/cmd/release/shared"
|
||||
"github.com/cli/cli/v2/pkg/httpmock"
|
||||
)
|
||||
|
||||
func TestNewCmdVerifyAsset_Args(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
wantTag string
|
||||
wantFile string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "valid args",
|
||||
args: []string{"v1.2.3", "../../attestation/test/data/a.zip"},
|
||||
wantTag: "v1.2.3",
|
||||
wantFile: "../../attestation/test/data/a.zip",
|
||||
},
|
||||
{
|
||||
name: "valid flag with no tag",
|
||||
|
||||
args: []string{"../../attestation/test/data/a.zip"},
|
||||
wantFile: "../../attestation/test/data/a.zip",
|
||||
},
|
||||
{
|
||||
name: "no args",
|
||||
args: []string{},
|
||||
wantErr: "you must specify an asset filepath",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
testIO, _, _, _ := iostreams.Test()
|
||||
var testReg httpmock.Registry
|
||||
var metaResp = api.MetaResponse{
|
||||
Domains: api.Domain{
|
||||
ArtifactAttestations: api.ArtifactAttestations{},
|
||||
},
|
||||
}
|
||||
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
|
||||
httpmock.StatusJSONResponse(200, &metaResp))
|
||||
|
||||
f := &cmdutil.Factory{
|
||||
IOStreams: testIO,
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
reg := &testReg
|
||||
client := &http.Client{}
|
||||
httpmock.ReplaceTripper(client, reg)
|
||||
return client, nil
|
||||
},
|
||||
BaseRepo: func() (ghrepo.Interface, error) {
|
||||
return ghrepo.FromFullName("owner/repo")
|
||||
},
|
||||
}
|
||||
|
||||
var opts *attestation.AttestOptions
|
||||
cmd := NewCmdVerifyAsset(f, func(o *attestation.AttestOptions) error {
|
||||
opts = o
|
||||
return nil
|
||||
})
|
||||
cmd.SetArgs(tt.args)
|
||||
cmd.SetIn(&bytes.Buffer{})
|
||||
cmd.SetOut(&bytes.Buffer{})
|
||||
cmd.SetErr(&bytes.Buffer{})
|
||||
_, err := cmd.ExecuteC()
|
||||
if tt.wantErr != "" {
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.wantErr)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.wantTag, opts.TagName)
|
||||
assert.Equal(t, tt.wantFile, opts.AssetFilePath)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_verifyAssetRun_Success(t *testing.T) {
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
tagName := "v1.2.3"
|
||||
|
||||
fakeHTTP := &httpmock.Registry{}
|
||||
defer fakeHTTP.Verify(t)
|
||||
fakeSHA := "1234567890abcdef1234567890abcdef12345678"
|
||||
shared.StubFetchRefSHA(t, fakeHTTP, "owner", "repo", tagName, fakeSHA)
|
||||
|
||||
baseRepo, err := ghrepo.FromFullName("owner/repo")
|
||||
require.NoError(t, err)
|
||||
|
||||
opts := &attestation.AttestOptions{
|
||||
TagName: tagName,
|
||||
AssetFilePath: "../../attestation/test/data/a.zip",
|
||||
Repo: "owner/repo",
|
||||
Owner: "owner",
|
||||
Limit: 10,
|
||||
Logger: io.NewHandler(ios),
|
||||
APIClient: api.NewTestClient(),
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
HttpClient: &http.Client{Transport: fakeHTTP},
|
||||
BaseRepo: baseRepo,
|
||||
}
|
||||
|
||||
err = verifyAssetRun(opts)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_verifyAssetRun_NoAttestation(t *testing.T) {
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
opts := &attestation.AttestOptions{
|
||||
TagName: "v1.2.3",
|
||||
AssetFilePath: "artifact.tgz",
|
||||
Repo: "owner/repo",
|
||||
Limit: 10,
|
||||
Logger: io.NewHandler(ios),
|
||||
IO: ios,
|
||||
APIClient: api.NewTestClient(),
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
EC: verification.EnforcementCriteria{},
|
||||
}
|
||||
|
||||
err := verifyAssetRun(opts)
|
||||
require.Error(t, err, "failed to get open local artifact: open artifact.tgz: no such file or director")
|
||||
}
|
||||
|
||||
func Test_getFileName(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{"foo/bar/baz.txt", "baz.txt"},
|
||||
{"baz.txt", "baz.txt"},
|
||||
{"/tmp/foo.tar.gz", "foo.tar.gz"},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.input, func(t *testing.T) {
|
||||
got := getFileName(tt.input)
|
||||
assert.Equal(t, tt.want, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -28,14 +28,12 @@ func NewCmdVerify(f *cmdutil.Factory, runF func(*attestation.AttestOptions) erro
|
|||
Use: "verify [<tag>]",
|
||||
Short: "Verify the attestation for a GitHub Release.",
|
||||
Hidden: true,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Args: cobra.MaximumNArgs(1),
|
||||
PreRunE: func(cmd *cobra.Command, args []string) error {
|
||||
if len(args) < 1 {
|
||||
return cmdutil.FlagErrorf("You must specify a tag")
|
||||
if len(args) > 0 {
|
||||
opts.TagName = args[0]
|
||||
}
|
||||
|
||||
opts.TagName = args[0]
|
||||
|
||||
httpClient, err := f.HttpClient()
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
@ -115,6 +113,14 @@ func NewCmdVerify(f *cmdutil.Factory, runF func(*attestation.AttestOptions) erro
|
|||
func verifyRun(opts *attestation.AttestOptions) error {
|
||||
ctx := context.Background()
|
||||
|
||||
if opts.TagName == "" {
|
||||
release, err := shared.FetchLatestRelease(ctx, opts.HttpClient, opts.BaseRepo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
opts.TagName = release.TagName
|
||||
}
|
||||
|
||||
ref, err := shared.FetchRefSHA(ctx, opts.HttpClient, opts.BaseRepo, opts.TagName)
|
||||
if err != nil {
|
||||
return err
|
||||
|
|
|
|||
142
pkg/cmd/release/verify/verify_test.go
Normal file
142
pkg/cmd/release/verify/verify_test.go
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
package verify
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
|
||||
"github.com/cli/cli/v2/internal/ghrepo"
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/api"
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/io"
|
||||
"github.com/cli/cli/v2/pkg/cmd/attestation/verification"
|
||||
"github.com/cli/cli/v2/pkg/cmd/release/attestation"
|
||||
"github.com/cli/cli/v2/pkg/cmd/release/shared"
|
||||
"github.com/cli/cli/v2/pkg/cmdutil"
|
||||
"github.com/cli/cli/v2/pkg/httpmock"
|
||||
"github.com/cli/cli/v2/pkg/iostreams"
|
||||
"github.com/stretchr/testify/require"
|
||||
"gotest.tools/v3/assert"
|
||||
)
|
||||
|
||||
func TestNewCmdVerify_Args(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
args []string
|
||||
wantTag string
|
||||
wantErr string
|
||||
}{
|
||||
{
|
||||
name: "valid tag arg",
|
||||
args: []string{"v1.2.3"},
|
||||
wantTag: "v1.2.3",
|
||||
},
|
||||
{
|
||||
name: "no tag arg",
|
||||
args: []string{},
|
||||
wantTag: "",
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
testIO, _, _, _ := iostreams.Test()
|
||||
var testReg httpmock.Registry
|
||||
var metaResp = api.MetaResponse{
|
||||
Domains: api.Domain{
|
||||
ArtifactAttestations: api.ArtifactAttestations{},
|
||||
},
|
||||
}
|
||||
testReg.Register(httpmock.REST(http.MethodGet, "meta"),
|
||||
httpmock.StatusJSONResponse(200, &metaResp))
|
||||
|
||||
f := &cmdutil.Factory{
|
||||
IOStreams: testIO,
|
||||
HttpClient: func() (*http.Client, error) {
|
||||
reg := &testReg
|
||||
client := &http.Client{}
|
||||
httpmock.ReplaceTripper(client, reg)
|
||||
return client, nil
|
||||
},
|
||||
BaseRepo: func() (ghrepo.Interface, error) {
|
||||
return ghrepo.FromFullName("owner/repo")
|
||||
},
|
||||
}
|
||||
|
||||
var opts *attestation.AttestOptions
|
||||
cmd := NewCmdVerify(f, func(o *attestation.AttestOptions) error {
|
||||
opts = o
|
||||
return nil
|
||||
})
|
||||
cmd.SetArgs(tt.args)
|
||||
cmd.SetIn(&bytes.Buffer{})
|
||||
cmd.SetOut(&bytes.Buffer{})
|
||||
cmd.SetErr(&bytes.Buffer{})
|
||||
_, err := cmd.ExecuteC()
|
||||
require.NoError(t, err)
|
||||
assert.Equal(t, tt.wantTag, opts.TagName)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_verifyRun_Success(t *testing.T) {
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
tagName := "v1.2.3"
|
||||
|
||||
fakeHTTP := &httpmock.Registry{}
|
||||
defer fakeHTTP.Verify(t)
|
||||
fakeSHA := "1234567890abcdef1234567890abcdef12345678"
|
||||
shared.StubFetchRefSHA(t, fakeHTTP, "owner", "repo", tagName, fakeSHA)
|
||||
|
||||
baseRepo, err := ghrepo.FromFullName("owner/repo")
|
||||
require.NoError(t, err)
|
||||
|
||||
opts := &attestation.AttestOptions{
|
||||
TagName: tagName,
|
||||
Repo: "owner/repo",
|
||||
Owner: "owner",
|
||||
Limit: 10,
|
||||
Logger: io.NewHandler(ios),
|
||||
APIClient: api.NewTestClient(),
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
HttpClient: &http.Client{Transport: fakeHTTP},
|
||||
BaseRepo: baseRepo,
|
||||
}
|
||||
|
||||
ec, err := attestation.NewEnforcementCriteria(opts)
|
||||
require.NoError(t, err)
|
||||
opts.EC = ec
|
||||
|
||||
err = verifyRun(opts)
|
||||
require.NoError(t, err)
|
||||
}
|
||||
|
||||
func Test_verifyRun_NoAttestation(t *testing.T) {
|
||||
ios, _, _, _ := iostreams.Test()
|
||||
tagName := "v1.2.3"
|
||||
|
||||
fakeHTTP := &httpmock.Registry{}
|
||||
defer fakeHTTP.Verify(t)
|
||||
fakeSHA := "1234567890abcdef1234567890abcdef12345678"
|
||||
shared.StubFetchRefSHA(t, fakeHTTP, "owner", "repo", tagName, fakeSHA)
|
||||
|
||||
baseRepo, err := ghrepo.FromFullName("owner/repo")
|
||||
require.NoError(t, err)
|
||||
|
||||
opts := &attestation.AttestOptions{
|
||||
TagName: tagName,
|
||||
Repo: "owner/repo",
|
||||
Owner: "owner",
|
||||
Limit: 10,
|
||||
Logger: io.NewHandler(ios),
|
||||
APIClient: api.NewFailTestClient(),
|
||||
SigstoreVerifier: verification.NewMockSigstoreVerifier(t),
|
||||
HttpClient: &http.Client{Transport: fakeHTTP},
|
||||
BaseRepo: baseRepo,
|
||||
}
|
||||
|
||||
ec, err := attestation.NewEnforcementCriteria(opts)
|
||||
require.NoError(t, err)
|
||||
opts.EC = ec
|
||||
|
||||
err = verifyRun(opts)
|
||||
require.Error(t, err, "failed to fetch attestations from owner/repo")
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue