vuln-list-update/alpine/alpine_test.go

411 lines
13 KiB
Go
Raw Normal View History

2019-10-10 18:45:17 +03:00
package alpine_test
2019-04-30 07:02:09 +03:00
import (
"encoding/json"
"errors"
2019-10-10 18:45:17 +03:00
"fmt"
2019-04-30 07:02:09 +03:00
"io/ioutil"
2019-10-10 18:45:17 +03:00
"os"
2019-04-30 07:02:09 +03:00
"path"
2019-10-10 18:45:17 +03:00
"path/filepath"
2019-04-30 07:02:09 +03:00
"reflect"
2019-10-10 18:45:17 +03:00
"strings"
2019-04-30 07:02:09 +03:00
"testing"
"golang.org/x/xerrors"
2019-10-10 18:45:17 +03:00
"github.com/aquasecurity/vuln-list-update/alpine"
"github.com/stretchr/testify/assert"
2019-10-10 18:45:17 +03:00
"github.com/stretchr/testify/mock"
2019-04-30 07:02:09 +03:00
)
type MockGitConfig struct {
2019-10-10 18:45:17 +03:00
mock.Mock
}
2020-12-17 18:05:35 +03:00
func (mgc *MockGitConfig) CloneOrPull(a string, b string, c string) (map[string]struct{}, error) {
args := mgc.Called(a, b, c)
2019-10-10 18:45:17 +03:00
return args.Get(0).(map[string]struct{}), args.Error(1)
}
2019-10-12 09:54:08 +03:00
func (mgc *MockGitConfig) RemoteBranch(a string) ([]string, error) {
2019-10-10 18:45:17 +03:00
args := mgc.Called(a)
return args.Get(0).([]string), args.Error(1)
}
2019-10-12 09:54:08 +03:00
func (mgc *MockGitConfig) Checkout(a string, b string) error {
2019-10-10 18:45:17 +03:00
args := mgc.Called(a, b)
return args.Error(0)
}
2019-10-07 09:54:14 +03:00
func TestParsePkgVerRel(t *testing.T) {
vectors := []struct {
file string // Test input file
pkgVer string
pkgRel string
secFixes map[string][]string
}{
{
2019-10-10 18:45:17 +03:00
file: "testdata/aports/main/freeradius/APKBUILD",
2019-10-07 09:54:14 +03:00
pkgVer: "3.0.19",
pkgRel: "0",
},
{
2019-10-10 18:45:17 +03:00
file: "testdata/aports/main/wireshark/APKBUILD",
2019-10-07 09:54:14 +03:00
pkgVer: "2.6.8",
pkgRel: "1",
},
}
for _, v := range vectors {
t.Run(path.Base(v.file), func(t *testing.T) {
content, err := ioutil.ReadFile(v.file)
if err != nil {
t.Fatalf("ReadAll() error: %v", err)
}
2019-10-10 18:45:17 +03:00
pkgVer, pkgRel, err := alpine.ParsePkgVerRel(&alpine.Config{}, string(content))
2019-10-07 09:54:14 +03:00
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if pkgVer != v.pkgVer {
t.Errorf("pkgVer: got %s, want %s", pkgVer, v.pkgVer)
}
if pkgRel != v.pkgRel {
t.Errorf("pkgRel: got %s, want %s", pkgRel, v.pkgRel)
}
})
}
}
func TestParseSecFixes(t *testing.T) {
2019-04-30 07:02:09 +03:00
vectors := []struct {
file string // Test input file
pkgVer string
pkgRel string
secFixes map[string][]string
}{
{
2019-10-10 18:45:17 +03:00
file: "testdata/aports/main/freeradius/APKBUILD",
2019-04-30 07:02:09 +03:00
pkgVer: "3.0.19",
pkgRel: "0",
secFixes: map[string][]string{
"3.0.19-r0": {"CVE-2019-11234", "CVE-2019-11235"},
},
},
{
2019-10-10 18:45:17 +03:00
file: "testdata/aports/main/wireshark/APKBUILD",
2019-04-30 07:02:09 +03:00
pkgVer: "2.6.8",
pkgRel: "1",
secFixes: map[string][]string{
"2.6.8-r0": {"CVE-2019-10894", "CVE-2019-10895", "CVE-2019-10896", "CVE-2019-10899", "CVE-2019-10901", "CVE-2019-10903"},
"2.6.7-r0": {"CVE-2019-9208", "CVE-2019-9209", "CVE-2019-9214"},
"2.6.6-r0": {"CVE-2019-5717", "CVE-2019-5718", "CVE-2019-5719", "CVE-2019-5721"},
},
},
{
file: "testdata/aports/main/libssh2/APKBUILD",
pkgVer: "1.9.0",
pkgRel: "1",
secFixes: map[string][]string{
"1.9.0-r1": {"CVE-2019-17498"},
"1.9.0-r0": {"CVE-2019-13115"},
},
},
2019-04-30 07:02:09 +03:00
}
for _, v := range vectors {
t.Run(path.Base(v.file), func(t *testing.T) {
content, err := ioutil.ReadFile(v.file)
if err != nil {
t.Fatalf("ReadAll() error: %v", err)
}
2019-10-10 18:45:17 +03:00
secFixes, err := alpine.ParseSecFixes(&alpine.Config{}, string(content))
2019-04-30 07:02:09 +03:00
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !reflect.DeepEqual(secFixes, v.secFixes) {
t.Errorf("secFixes: got %v, want %v", secFixes, v.secFixes)
}
})
}
}
func TestShouldOverwrite(t *testing.T) {
2019-10-10 18:45:17 +03:00
testCases := []struct {
name string
currentVersion string
issuedAdvisory interface{}
expctedOverwrite bool
}{
{
name: "issued advisory should overwrite existing one with valid version",
currentVersion: "1.0.0",
issuedAdvisory: alpine.Advisory{
IssueID: 0,
VulnerabilityID: "CVE-2100-0001",
Release: "1.0",
Package: "testpackage",
Repository: "main",
FixedVersion: "1.2.0",
Description: "for testing only",
},
expctedOverwrite: true,
},
{
name: "issued advisory should overwrite existing one with valid version having a suffix",
currentVersion: "1.1_beta1",
issuedAdvisory: alpine.Advisory{
IssueID: 0,
VulnerabilityID: "CVE-2100-0001",
Release: "1.0",
Package: "testpackage",
Repository: "main",
FixedVersion: "1.1",
Description: "for testing only",
},
expctedOverwrite: true,
},
2019-10-10 18:45:17 +03:00
{
name: "issued advisory should NOT overwrite existing one with valid version",
currentVersion: "1.0.0",
issuedAdvisory: alpine.Advisory{
IssueID: 0,
VulnerabilityID: "CVE-2100-0001",
Release: "1.0",
Package: "testpackage",
Repository: "main",
FixedVersion: "0.9.0",
Description: "for testing only",
},
expctedOverwrite: false,
},
{
name: "invalid advisory json",
currentVersion: "1.0.0",
issuedAdvisory: []byte(`badjsonhere`),
expctedOverwrite: true,
},
{
name: "empty fixed version",
currentVersion: "1.0.0",
issuedAdvisory: alpine.Advisory{
Subject: "non empty subject",
},
expctedOverwrite: true,
},
{
name: "invalid old advisory version",
currentVersion: "1.0.0",
issuedAdvisory: alpine.Advisory{
Subject: "non empty subject",
Package: "test",
FixedVersion: "invalid",
},
2019-10-10 18:45:17 +03:00
expctedOverwrite: false,
},
{
name: "invalid current advisory version",
currentVersion: "invalid",
issuedAdvisory: alpine.Advisory{
Subject: "non empty subject",
Package: "test",
FixedVersion: "1.0.0",
},
2019-10-10 18:45:17 +03:00
expctedOverwrite: false,
},
}
2019-10-10 18:45:17 +03:00
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
f, _ := ioutil.TempFile("", "TestShouldOverwrite_happy_sad")
2019-10-10 18:45:17 +03:00
defer os.Remove(f.Name())
b, _ := json.Marshal(tc.issuedAdvisory)
_, _ = f.Write(b)
2019-10-10 18:45:17 +03:00
assert.Equal(t, tc.expctedOverwrite, alpine.ShouldOverwrite(&alpine.Config{}, f.Name(), tc.currentVersion), tc.name)
assert.NoError(t, f.Close())
})
2019-10-10 18:45:17 +03:00
}
}
func TestWalkApkBuild(t *testing.T) {
2019-10-10 18:45:17 +03:00
advisories, err := alpine.WalkApkBuild(&alpine.Config{}, "testdata/aports", "1.0.0")
assert.NoError(t, err)
2019-10-10 18:45:17 +03:00
assert.ElementsMatch(t, []alpine.Advisory{
{FixedVersion: "1.2.15-r11", VulnerabilityID: "CVE-2019-7572", Release: "1.0.0", Package: "sdl", Repository: "main"},
{FixedVersion: "1.2.15-r11", VulnerabilityID: "CVE-2019-7574", Release: "1.0.0", Package: "sdl", Repository: "main"},
{FixedVersion: "2.6.8-r0", VulnerabilityID: "CVE-2019-10894", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.8-r0", VulnerabilityID: "CVE-2019-10895", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.8-r0", VulnerabilityID: "CVE-2019-10896", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.8-r0", VulnerabilityID: "CVE-2019-10899", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.8-r0", VulnerabilityID: "CVE-2019-10901", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.8-r0", VulnerabilityID: "CVE-2019-10903", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.7-r0", VulnerabilityID: "CVE-2019-9208", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.7-r0", VulnerabilityID: "CVE-2019-9209", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.7-r0", VulnerabilityID: "CVE-2019-9214", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.6-r0", VulnerabilityID: "CVE-2019-5717", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.6-r0", VulnerabilityID: "CVE-2019-5718", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.6-r0", VulnerabilityID: "CVE-2019-5719", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "2.6.6-r0", VulnerabilityID: "CVE-2019-5721", Release: "1.0.0", Package: "wireshark", Repository: "main"},
{FixedVersion: "3.0.19-r0", VulnerabilityID: "CVE-2019-11234", Release: "1.0.0", Package: "freeradius", Repository: "main"},
{FixedVersion: "3.0.19-r0", VulnerabilityID: "CVE-2019-11235", Release: "1.0.0", Package: "freeradius", Repository: "main"},
{FixedVersion: "1.9.0-r0", VulnerabilityID: "CVE-2019-13115", Release: "1.0.0", Package: "libssh2", Repository: "main"},
{FixedVersion: "1.9.0-r1", VulnerabilityID: "CVE-2019-17498", Release: "1.0.0", Package: "libssh2", Repository: "main"},
2019-10-10 18:45:17 +03:00
{FixedVersion: "1.7.3-r0", VulnerabilityID: "CVE-2019-9917", Release: "1.0.0", Package: "znc", Repository: "community"},
{FixedVersion: "1.7.1-r0", VulnerabilityID: "CVE-2018-14055", Release: "1.0.0", Package: "znc", Repository: "community"},
{FixedVersion: "1.7.1-r0", VulnerabilityID: "CVE-2018-14056", Release: "1.0.0", Package: "znc", Repository: "community"},
},
advisories)
}
func TestBuildAdvisories(t *testing.T) {
secFixes := map[string][]string{
"2.6.8-r0": {"CVE-2019-10894"},
2019-10-10 18:45:17 +03:00
"2.6.7-r1": {"CVE_2019-2426 XSA-201"}, // typo
"2.6.5-r0": {"CVE_2019-5910 (+ some extra in parens)"},
}
2019-10-10 18:45:17 +03:00
assert.ElementsMatch(t, []alpine.Advisory{
{IssueID: 0, VulnerabilityID: "CVE-2019-10894", Release: "1.0.0", Package: "testpkg", Repository: "testrepo", FixedVersion: "2.6.8-r0", Subject: "", Description: ""},
2019-10-10 18:45:17 +03:00
{IssueID: 0, VulnerabilityID: "CVE-2019-2426", Release: "1.0.0", Package: "testpkg", Repository: "testrepo", FixedVersion: "2.6.7-r1", Subject: "", Description: ""},
{IssueID: 0, VulnerabilityID: "XSA-201", Release: "1.0.0", Package: "testpkg", Repository: "testrepo", FixedVersion: "2.6.7-r1", Subject: "", Description: ""},
{IssueID: 0, VulnerabilityID: "CVE-2019-5910", Release: "1.0.0", Package: "testpkg", Repository: "testrepo", FixedVersion: "2.6.5-r0", Subject: "", Description: ""}},
2019-10-10 18:45:17 +03:00
alpine.BuildAdvisories(&alpine.Config{}, secFixes, "1.0.0", "testpkg", "testrepo"))
}
2019-10-10 18:45:17 +03:00
func TestConfig_Update(t *testing.T) {
type cloneOrPull struct {
returnArg map[string]struct{}
err error
}
type remoteBranch struct {
returnArg []string
err error
}
2019-10-10 18:45:17 +03:00
testCases := []struct {
name string
remoteBranch remoteBranch // mock value
cloneOrPull cloneOrPull // mock value
checkout map[string]error // mock value
wantErr error
}{
{
name: "happy path",
remoteBranch: remoteBranch{
returnArg: []string{"origin/branch1-stable", "origin/branch2", "origin/branch3"},
},
checkout: map[string]error{mock.Anything: nil},
wantErr: nil,
},
{
name: "invalid branch name",
remoteBranch: remoteBranch{returnArg: []string{"badbranch-stable"}},
checkout: map[string]error{mock.Anything: nil},
wantErr: nil,
},
{
name: "git fails to show remote branches",
remoteBranch: remoteBranch{
returnArg: nil, err: errors.New("failed to show remote branch"),
},
checkout: map[string]error{mock.Anything: nil},
wantErr: xerrors.Errorf("failed to show branches: %w", errors.New("failed to show remote branch")),
},
{
name: "git clone fails",
cloneOrPull: cloneOrPull{
returnArg: nil, err: errors.New("failed clone operation"),
},
checkout: map[string]error{mock.Anything: nil},
wantErr: xerrors.Errorf("failed to clone alpine repository: %w", errors.New("failed clone operation")),
},
{
name: "git fails to checkout branch",
remoteBranch: remoteBranch{
returnArg: []string{"origin/branch1-stable", "origin/branch2", "origin/branch3"},
},
2019-10-10 18:45:17 +03:00
checkout: map[string]error{mock.Anything: errors.New("failed to checkout branch")},
wantErr: xerrors.Errorf("git failed to checkout branch: %w", errors.New("failed to checkout branch")),
},
{
name: "git checkout of a particular branch fails",
remoteBranch: remoteBranch{
returnArg: []string{"origin/branch1-stable", "origin/branch2", "origin/branch3"},
},
2019-10-10 18:45:17 +03:00
checkout: map[string]error{
"master": errors.New("failed to checkout master"),
"origin/branch1-stable": errors.New("failed to checkout branch1-stable"),
},
2019-10-10 18:45:17 +03:00
wantErr: xerrors.Errorf("git failed to checkout branch: %w", errors.New("failed to checkout branch1-stable")),
},
}
cacheDir := "testdata"
repoDir := filepath.Join(cacheDir, "aports")
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
vulnListDir, err := ioutil.TempDir("", "TestUpdate")
assert.NoError(t, err)
defer os.RemoveAll(vulnListDir)
mockGitConfig := new(MockGitConfig)
// setup expectations with a placeholder in the argument list
mockGitConfig.On("RemoteBranch", repoDir).Return(
tc.remoteBranch.returnArg, tc.remoteBranch.err)
2020-12-17 18:05:35 +03:00
mockGitConfig.On("CloneOrPull", mock.Anything, repoDir, "master").Return(
2019-10-10 18:45:17 +03:00
tc.cloneOrPull.returnArg, tc.cloneOrPull.err)
for arg, returnErr := range tc.checkout {
mockGitConfig.On("Checkout", repoDir, arg).Return(returnErr)
}
ac := alpine.Config{
GitClient: mockGitConfig,
CacheDir: cacheDir,
VulnListDir: vulnListDir,
}
fmt.Println(vulnListDir)
err = ac.Update()
if tc.wantErr != nil {
assert.EqualError(t, err, tc.wantErr.Error())
} else {
assert.NoError(t, err)
err = filepath.Walk(vulnListDir, func(path string, info os.FileInfo, err error) error {
if err != nil {
return err
}
if info.IsDir() {
return nil
}
paths := strings.Split(path, string(os.PathSeparator))
assert.True(t, len(paths) > 3)
golden := filepath.Join("testdata", "goldens",
paths[len(paths)-3], paths[len(paths)-2], paths[len(paths)-1],
)
got, _ := ioutil.ReadFile(path)
want, _ := ioutil.ReadFile(golden + ".golden")
assert.Equal(t, string(want), string(got), "Alpine result json")
return nil
})
assert.NoError(t, err)
}
})
}
}