mirror of
https://github.com/aquasecurity/trivy.git
synced 2025-12-22 15:16:33 -08:00
feat(db): download a db file from GitHub Release
This commit is contained in:
466
pkg/github/github_test.go
Normal file
466
pkg/github/github_test.go
Normal file
@@ -0,0 +1,466 @@
|
||||
package github
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"net/url"
|
||||
"path"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/aquasecurity/trivy/pkg/log"
|
||||
"golang.org/x/xerrors"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/google/go-github/v28/github"
|
||||
"github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
type MockRepository struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
func (_m *MockRepository) ListReleases(ctx context.Context, opt *github.ListOptions) (
|
||||
[]*github.RepositoryRelease, *github.Response, error) {
|
||||
ret := _m.Called(ctx, opt)
|
||||
ret0 := ret.Get(0)
|
||||
if ret0 == nil {
|
||||
return nil, nil, ret.Error(2)
|
||||
}
|
||||
releases, ok := ret0.([]*github.RepositoryRelease)
|
||||
if !ok {
|
||||
return nil, nil, ret.Error(2)
|
||||
}
|
||||
return releases, nil, ret.Error(2)
|
||||
}
|
||||
|
||||
func (_m *MockRepository) DownloadAsset(ctx context.Context, id int64) (io.ReadCloser, string, error) {
|
||||
ret := _m.Called(ctx, id)
|
||||
ret0 := ret.Get(0)
|
||||
if ret0 == nil {
|
||||
return nil, ret.String(1), ret.Error(2)
|
||||
}
|
||||
rc, ok := ret0.(io.ReadCloser)
|
||||
if !ok {
|
||||
return nil, ret.String(1), ret.Error(2)
|
||||
}
|
||||
return rc, ret.String(1), ret.Error(2)
|
||||
}
|
||||
|
||||
func TestClient_DownloadDB(t *testing.T) {
|
||||
type listReleasesOutput struct {
|
||||
releases []*github.RepositoryRelease
|
||||
response *github.Response
|
||||
err error
|
||||
}
|
||||
type listReleases struct {
|
||||
input string
|
||||
output listReleasesOutput
|
||||
}
|
||||
|
||||
type downloadAssetOutput struct {
|
||||
rc io.ReadCloser
|
||||
redirectPath string
|
||||
err error
|
||||
}
|
||||
type downloadAsset struct {
|
||||
input int64
|
||||
output downloadAssetOutput
|
||||
}
|
||||
|
||||
testCases := []struct {
|
||||
name string
|
||||
fileName string
|
||||
filePaths []string
|
||||
listReleases []listReleases
|
||||
downloadAsset []downloadAsset
|
||||
expectedError error
|
||||
}{
|
||||
{
|
||||
name: "happy path",
|
||||
fileName: "trivy.db.gz",
|
||||
listReleases: []listReleases{
|
||||
{
|
||||
input: mock.Anything,
|
||||
output: listReleasesOutput{
|
||||
releases: []*github.RepositoryRelease{
|
||||
{
|
||||
// this release should be skipped due to the wrong prefix of the tag
|
||||
ID: github.Int64(2),
|
||||
Name: github.String("v2-2020010101"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 1, 1, 1, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(200),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: github.Int64(1),
|
||||
Name: github.String("v1-2020123123"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
downloadAsset: []downloadAsset{
|
||||
{
|
||||
input: 100,
|
||||
output: downloadAssetOutput{
|
||||
rc: ioutil.NopCloser(strings.NewReader("foo")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with redirect URL",
|
||||
fileName: "trivy.db.gz",
|
||||
listReleases: []listReleases{
|
||||
{
|
||||
input: mock.Anything,
|
||||
output: listReleasesOutput{
|
||||
releases: []*github.RepositoryRelease{
|
||||
{
|
||||
ID: github.Int64(1),
|
||||
Name: github.String("v1-2020123123"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
downloadAsset: []downloadAsset{
|
||||
{
|
||||
input: 100,
|
||||
output: downloadAssetOutput{
|
||||
redirectPath: "/happy",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with inorder releases",
|
||||
fileName: "trivy.db.gz",
|
||||
listReleases: []listReleases{
|
||||
{
|
||||
input: mock.Anything,
|
||||
output: listReleasesOutput{
|
||||
releases: []*github.RepositoryRelease{
|
||||
{
|
||||
ID: github.Int64(1),
|
||||
Name: github.String("v1-2019100123"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2019, 10, 1, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
// this release should be used because this is the latest
|
||||
ID: github.Int64(3),
|
||||
Name: github.String("v1-2019100200"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2019, 10, 2, 0, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(300),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ID: github.Int64(2),
|
||||
Name: github.String("v1-2019100122"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2019, 10, 1, 22, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(200),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
downloadAsset: []downloadAsset{
|
||||
{
|
||||
input: 300,
|
||||
output: downloadAssetOutput{
|
||||
rc: ioutil.NopCloser(strings.NewReader("foo")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "happy path with no asset",
|
||||
fileName: "trivy.db.gz",
|
||||
listReleases: []listReleases{
|
||||
{
|
||||
input: mock.Anything,
|
||||
output: listReleasesOutput{
|
||||
releases: []*github.RepositoryRelease{
|
||||
{
|
||||
// this release should be skipped due to no asset
|
||||
ID: github.Int64(1),
|
||||
Name: github.String("v1-2019100123"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2019, 10, 1, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
{
|
||||
// this release should be skipped due to no asset
|
||||
ID: github.Int64(3),
|
||||
Name: github.String("v1-2019100200"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2019, 10, 2, 0, 59, 59, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
{
|
||||
// this release should be used because this release has assets
|
||||
ID: github.Int64(2),
|
||||
Name: github.String("v1-2019100122"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2019, 10, 1, 22, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(200),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
downloadAsset: []downloadAsset{
|
||||
{
|
||||
input: 200,
|
||||
output: downloadAssetOutput{
|
||||
rc: ioutil.NopCloser(strings.NewReader("foo")),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no asset",
|
||||
fileName: "trivy.db.gz",
|
||||
listReleases: []listReleases{
|
||||
{
|
||||
input: mock.Anything,
|
||||
output: listReleasesOutput{
|
||||
releases: []*github.RepositoryRelease{
|
||||
{
|
||||
ID: github.Int64(1),
|
||||
Name: github.String("v1-2020123000"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: xerrors.New("DB file not found"),
|
||||
},
|
||||
{
|
||||
name: "the file name doesn't match",
|
||||
fileName: "trivy-light.db.gz",
|
||||
listReleases: []listReleases{
|
||||
{
|
||||
input: mock.Anything,
|
||||
output: listReleasesOutput{
|
||||
releases: []*github.RepositoryRelease{
|
||||
{
|
||||
ID: github.Int64(1),
|
||||
Name: github.String("v1-2020123000"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: xerrors.New("DB file not found"),
|
||||
},
|
||||
{
|
||||
name: "ListReleases returns error",
|
||||
fileName: "trivy.db.gz",
|
||||
listReleases: []listReleases{
|
||||
{
|
||||
input: mock.Anything,
|
||||
output: listReleasesOutput{
|
||||
err: xerrors.New("something wrong"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: xerrors.New("failed to list releases: something wrong"),
|
||||
},
|
||||
{
|
||||
name: "DownloadAsset returns error",
|
||||
fileName: "trivy.db.gz",
|
||||
listReleases: []listReleases{
|
||||
{
|
||||
input: mock.Anything,
|
||||
output: listReleasesOutput{
|
||||
releases: []*github.RepositoryRelease{
|
||||
{
|
||||
ID: github.Int64(1),
|
||||
Name: github.String("v1-2020123000"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
downloadAsset: []downloadAsset{
|
||||
{
|
||||
input: 100,
|
||||
output: downloadAssetOutput{
|
||||
err: xerrors.New("something wrong"),
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: xerrors.New("DB file not found"),
|
||||
},
|
||||
{
|
||||
name: "404 error",
|
||||
fileName: "trivy.db.gz",
|
||||
listReleases: []listReleases{
|
||||
{
|
||||
input: mock.Anything,
|
||||
output: listReleasesOutput{
|
||||
releases: []*github.RepositoryRelease{
|
||||
{
|
||||
ID: github.Int64(1),
|
||||
Name: github.String("v1-2020123000"),
|
||||
PublishedAt: &github.Timestamp{
|
||||
Time: time.Date(2020, 12, 31, 23, 59, 59, 0, time.UTC),
|
||||
},
|
||||
Assets: []github.ReleaseAsset{
|
||||
{
|
||||
ID: github.Int64(100),
|
||||
Name: github.String("trivy.db.gz"),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
downloadAsset: []downloadAsset{
|
||||
{
|
||||
input: 100,
|
||||
output: downloadAssetOutput{
|
||||
redirectPath: "/not_found",
|
||||
},
|
||||
},
|
||||
},
|
||||
expectedError: xerrors.New("DB file not found"),
|
||||
},
|
||||
}
|
||||
|
||||
if err := log.InitLogger(false, true); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
switch r.URL.Path {
|
||||
case "/happy":
|
||||
_, _ = fmt.Fprintf(w, "happy")
|
||||
case "/not_found":
|
||||
http.NotFound(w, r)
|
||||
}
|
||||
return
|
||||
},
|
||||
))
|
||||
defer ts.Close()
|
||||
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockRepo := new(MockRepository)
|
||||
for _, lr := range tc.listReleases {
|
||||
mockRepo.On("ListReleases", mock.Anything, lr.input).Return(
|
||||
lr.output.releases, lr.output.response, lr.output.err,
|
||||
)
|
||||
}
|
||||
for _, da := range tc.downloadAsset {
|
||||
var redirectURL string
|
||||
if da.output.redirectPath != "" {
|
||||
u, _ := url.Parse(ts.URL)
|
||||
u.Path = path.Join(u.Path, da.output.redirectPath)
|
||||
redirectURL = u.String()
|
||||
}
|
||||
mockRepo.On("DownloadAsset", mock.Anything, da.input).Return(
|
||||
da.output.rc, redirectURL, da.output.err,
|
||||
)
|
||||
}
|
||||
|
||||
client := Client{
|
||||
Repository: mockRepo,
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
rc, err := client.DownloadDB(ctx, tc.fileName)
|
||||
|
||||
switch {
|
||||
case tc.expectedError != nil:
|
||||
assert.EqualError(t, err, tc.expectedError.Error(), tc.name)
|
||||
default:
|
||||
assert.NoError(t, err, tc.name)
|
||||
assert.NotNil(t, rc, tc.name)
|
||||
}
|
||||
|
||||
mockRepo.AssertExpectations(t)
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user