github.com/khulnasoft-lab/tunnel-db@v0.0.0-20231117205118-74e1113bd007/pkg/vulndb/db_test.go (about) 1 package vulndb_test 2 3 import ( 4 "encoding/json" 5 "os" 6 "path/filepath" 7 "strings" 8 "testing" 9 "time" 10 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 "golang.org/x/xerrors" 14 "k8s.io/utils/clock" 15 fake "k8s.io/utils/clock/testing" 16 17 "github.com/khulnasoft-lab/tunnel-db/pkg/db" 18 "github.com/khulnasoft-lab/tunnel-db/pkg/dbtest" 19 "github.com/khulnasoft-lab/tunnel-db/pkg/metadata" 20 "github.com/khulnasoft-lab/tunnel-db/pkg/types" 21 "github.com/khulnasoft-lab/tunnel-db/pkg/vulndb" 22 "github.com/khulnasoft-lab/tunnel-db/pkg/vulnsrc" 23 "github.com/khulnasoft-lab/tunnel-db/pkg/vulnsrc/vulnerability" 24 ) 25 26 type fakeVulnSrc struct{} 27 28 func (f fakeVulnSrc) Name() types.SourceID { return "fake" } 29 30 func (f fakeVulnSrc) Update(dir string) error { 31 if strings.Contains(dir, "bad") { 32 return xerrors.New("something bad") 33 } 34 return nil 35 } 36 37 func TestTunnelDB_Insert(t *testing.T) { 38 type fields struct { 39 cacheDir string 40 clock clock.Clock 41 } 42 type args struct { 43 targets []string 44 } 45 tests := []struct { 46 name string 47 fields fields 48 args args 49 want metadata.Metadata 50 wantErr string 51 }{ 52 { 53 name: "happy path", 54 fields: fields{ 55 cacheDir: "happy", 56 clock: fake.NewFakeClock(time.Date(2021, 1, 2, 3, 4, 5, 0, time.UTC)), 57 }, 58 args: args{ 59 targets: []string{"fake"}, 60 }, 61 want: metadata.Metadata{ 62 Version: db.SchemaVersion, 63 NextUpdate: time.Date(2021, 1, 2, 15, 4, 5, 0, time.UTC), 64 UpdatedAt: time.Date(2021, 1, 2, 3, 4, 5, 0, time.UTC), 65 }, 66 }, 67 { 68 name: "sad path: unknown source", 69 fields: fields{ 70 cacheDir: "sad", 71 clock: fake.NewFakeClock(time.Date(2021, 1, 2, 3, 4, 5, 0, time.UTC)), 72 }, 73 args: args{ 74 targets: []string{"unknown"}, 75 }, 76 wantErr: "unknown is not supported", 77 }, 78 { 79 name: "sad path: update error", 80 fields: fields{ 81 cacheDir: "bad", 82 clock: fake.NewFakeClock(time.Date(2021, 1, 2, 3, 4, 5, 0, time.UTC)), 83 }, 84 args: args{ 85 targets: []string{"fake"}, 86 }, 87 wantErr: "fake update error", 88 }, 89 } 90 91 for _, tt := range tests { 92 t.Run(tt.name, func(t *testing.T) { 93 vulnsrcs := map[types.SourceID]vulnsrc.VulnSrc{ 94 "fake": fakeVulnSrc{}, 95 } 96 cacheDir := filepath.Join(t.TempDir(), tt.fields.cacheDir) 97 98 require.NoError(t, db.Init(cacheDir)) 99 defer db.Close() 100 101 c := vulndb.New(cacheDir, 12*time.Hour, vulndb.WithClock(tt.fields.clock), vulndb.WithVulnSrcs(vulnsrcs)) 102 err := c.Insert(tt.args.targets) 103 if tt.wantErr != "" { 104 require.NotNil(t, err) 105 assert.Contains(t, err.Error(), tt.wantErr) 106 return 107 } 108 require.NoError(t, err) 109 110 f, err := os.Open(metadata.Path(cacheDir)) 111 require.NoError(t, err) 112 113 // Compare metadata JSON file 114 var got metadata.Metadata 115 err = json.NewDecoder(f).Decode(&got) 116 require.NoError(t, err) 117 118 assert.Equal(t, tt.want, got) 119 }) 120 } 121 } 122 123 func TestTunnelDB_Build(t *testing.T) { 124 modified := time.Date(2020, 8, 24, 17, 37, 0, 0, time.UTC) 125 published := time.Date(2019, 4, 7, 0, 29, 0, 0, time.UTC) 126 127 type wantKV struct { 128 key []string 129 value interface{} 130 } 131 tests := []struct { 132 name string 133 fixtures []string 134 wantValues []wantKV 135 wantErr string 136 }{ 137 { 138 name: "happy path", 139 fixtures: []string{ 140 "testdata/fixtures/happy/vulnid.yaml", 141 "testdata/fixtures/happy/vulnerability-detail.yaml", 142 "testdata/fixtures/happy/advisory-detail.yaml", 143 }, 144 wantValues: []wantKV{ 145 { 146 key: []string{"Red Hat Enterprise Linux 8", "python-jinja2", "CVE-2019-10906"}, 147 value: types.Advisory{ 148 FixedVersion: "2.10.1-2.el8_0", 149 }, 150 }, 151 { 152 key: []string{"vulnerability", "CVE-2019-10906"}, 153 value: types.Vulnerability{ 154 Title: "python-jinja2: str.format_map allows sandbox escape", 155 Description: "In Pallets Jinja before 2.10.1, str.format_map allows a sandbox escape.", 156 Severity: "HIGH", 157 VendorSeverity: map[types.SourceID]types.Severity{ 158 vulnerability.NVD: types.SeverityHigh, 159 vulnerability.RedHat: types.SeverityCritical, 160 }, 161 PublishedDate: &published, 162 LastModifiedDate: &modified, 163 }, 164 }, 165 }, 166 }, 167 { 168 name: "broken advisory detail", 169 fixtures: []string{ 170 "testdata/fixtures/happy/vulnid.yaml", 171 "testdata/fixtures/happy/vulnerability-detail.yaml", 172 "testdata/fixtures/sad/advisory-detail.yaml", 173 }, 174 wantErr: "failed to unmarshall the advisory detail", 175 }, 176 { 177 name: "missing advisory detail", 178 fixtures: []string{ 179 "testdata/fixtures/happy/vulnid.yaml", 180 "testdata/fixtures/happy/vulnerability-detail.yaml", 181 }, 182 wantErr: "failed to delete advisory detail bucket", 183 }, 184 } 185 186 for _, tt := range tests { 187 t.Run(tt.name, func(t *testing.T) { 188 cacheDir := dbtest.InitDB(t, tt.fixtures) 189 defer db.Close() 190 191 full := vulndb.New(cacheDir, 12*time.Hour) 192 err := full.Build(nil) 193 if tt.wantErr != "" { 194 require.NotNil(t, err) 195 assert.Contains(t, err.Error(), tt.wantErr) 196 return 197 } 198 require.NoError(t, err) 199 200 // Compare DB entries 201 require.NoError(t, db.Close()) 202 dbPath := db.Path(cacheDir) 203 for _, want := range tt.wantValues { 204 dbtest.JSONEq(t, dbPath, want.key, want.value) 205 } 206 207 // Ensure that temporal buckets are removed 208 dbtest.NoBucket(t, dbPath, []string{"advisory-detail"}) 209 dbtest.NoBucket(t, dbPath, []string{"vulnerability-detail"}) 210 dbtest.NoBucket(t, dbPath, []string{"vulnerability-id"}) 211 }) 212 } 213 }