2019-10-10 18:45:17 +03:00
package alpine_test
2019-04-30 07:02:09 +03:00
import (
2019-10-07 23:59:50 +03:00
"encoding/json"
2019-10-08 03:28:23 +03:00
"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"
2019-10-07 23:59:50 +03:00
2019-10-08 03:28:23 +03:00
"golang.org/x/xerrors"
2019-10-10 18:45:17 +03:00
"github.com/aquasecurity/vuln-list-update/alpine"
2019-10-07 23:59:50 +03:00
"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
)
2019-10-08 03:28:23 +03:00
type MockGitConfig struct {
2019-10-10 18:45:17 +03:00
mock . Mock
2019-10-08 03:28:23 +03:00
}
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-08 03:28:23 +03:00
}
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-08 03:28:23 +03:00
}
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-08 03:28:23 +03:00
}
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" } ,
} ,
} ,
2020-06-29 20:54:35 +03:00
{
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 )
}
} )
}
}
2019-10-07 23:59:50 +03:00
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 ,
} ,
2020-06-29 20:55:16 +03:00
{
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-07 23:59:50 +03:00
} ,
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-07 23:59:50 +03:00
} ,
2019-10-10 18:45:17 +03:00
expctedOverwrite : false ,
} ,
}
2019-10-07 23:59:50 +03:00
2019-10-10 18:45:17 +03:00
for _ , tc := range testCases {
t . Run ( tc . name , func ( t * testing . T ) {
2019-10-07 23:59:50 +03:00
f , _ := ioutil . TempFile ( "" , "TestShouldOverwrite_happy_sad" )
2019-10-10 18:45:17 +03:00
defer os . Remove ( f . Name ( ) )
b , _ := json . Marshal ( tc . issuedAdvisory )
2019-10-07 23:59:50 +03:00
_ , _ = 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-07 23:59:50 +03:00
} )
2019-10-10 18:45:17 +03:00
}
2019-10-07 23:59:50 +03:00
}
2019-10-08 00:26:02 +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" )
2019-10-08 00:26:02 +03:00
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" } ,
2020-06-29 20:54:35 +03:00
{ 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 )
2019-10-08 00:26:02 +03:00
}
2019-10-08 00:57:48 +03:00
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
2019-10-08 00:57:48 +03:00
"2.6.5-r0" : { "CVE_2019-5910 (+ some extra in parens)" } ,
}
2019-10-10 18:45:17 +03:00
assert . ElementsMatch ( t , [ ] alpine . Advisory {
2019-10-08 00:57:48 +03:00
{ 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 : "" } ,
2019-10-08 00:57:48 +03:00
{ 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-08 00:57:48 +03:00
}
2019-10-08 03:28:23 +03:00
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-08 03:28:23 +03:00
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-08 03:30:20 +03:00
} ,
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-08 03:28:23 +03:00
} ,
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-08 03:28:23 +03:00
} ,
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 )
}
} )
}
2019-10-08 03:28:23 +03:00
}