github.com/quay/claircore@v1.5.28/datastore/postgres/affected_manifests_e2e_test.go (about) 1 package postgres 2 3 import ( 4 "context" 5 "encoding/json" 6 "os" 7 "path/filepath" 8 "testing" 9 10 "github.com/jackc/pgtype" 11 "github.com/jackc/pgx/v4/pgxpool" 12 "github.com/quay/zlog" 13 14 "github.com/quay/claircore" 15 "github.com/quay/claircore/indexer" 16 "github.com/quay/claircore/pkg/omnimatcher" 17 "github.com/quay/claircore/test/integration" 18 pgtest "github.com/quay/claircore/test/postgres" 19 ) 20 21 type affectedE2E struct { 22 store indexer.Store 23 pool *pgxpool.Pool 24 ctx context.Context 25 ir claircore.IndexReport 26 vr claircore.VulnerabilityReport 27 } 28 29 func TestAffectedE2E(t *testing.T) { 30 integration.NeedDB(t) 31 ctx := zlog.Test(context.Background(), t) 32 pool := pgtest.TestIndexerDB(ctx, t) 33 store := NewIndexerStore(pool) 34 35 table := []struct { 36 // name of the defined affectedE2E test 37 name string 38 // file name of index report in ./testdata 39 irFName string 40 // file name of vuln report in ./testdata 41 vrFName string 42 }{ 43 // these fixtures 44 // were generated against the same database 45 // to ensure all ids are sequentially increasing 46 // 47 // if fixtures are added you must generate 48 // this current set *and* your new fixtures against the same database 49 // to ensure there are no ID overlaps 50 // 51 // generate them via go generate github.com/quay/claircore/datastore/postgres 52 { 53 name: "amazonlinux 1", 54 irFName: "docker.io-library-amazonlinux-1.index.json", 55 vrFName: "docker.io-library-amazonlinux-1.report.json", 56 }, 57 { 58 name: "debian 8", 59 irFName: "docker.io-library-debian-8.index.json", 60 vrFName: "docker.io-library-debian-8.report.json", 61 }, 62 { 63 name: "debian 9", 64 irFName: "docker.io-library-debian-9.index.json", 65 vrFName: "docker.io-library-debian-9.report.json", 66 }, 67 { 68 name: "debian 10", 69 irFName: "docker.io-library-debian-10.index.json", 70 vrFName: "docker.io-library-debian-10.report.json", 71 }, 72 { 73 name: "ubi 8", 74 irFName: "registry.access.redhat.com-ubi8-ubi.index.json", 75 vrFName: "registry.access.redhat.com-ubi8-ubi.report.json", 76 }, 77 { 78 name: "ubuntu 16.04", 79 irFName: "docker.io-library-ubuntu-16.04.index.json", 80 vrFName: "docker.io-library-ubuntu-16.04.report.json", 81 }, 82 { 83 name: "ubuntu 18.04", 84 irFName: "docker.io-library-ubuntu-18.04.index.json", 85 vrFName: "docker.io-library-ubuntu-18.04.report.json", 86 }, 87 { 88 name: "ubuntu 19.10", 89 irFName: "docker.io-library-ubuntu-19.10.index.json", 90 vrFName: "docker.io-library-ubuntu-19.10.report.json", 91 }, 92 { 93 name: "ubuntu 20.04", 94 irFName: "docker.io-library-ubuntu-20.04.index.json", 95 vrFName: "docker.io-library-ubuntu-20.04.report.json", 96 }, 97 { 98 name: "mitmproxy 4.0.1", 99 irFName: "docker.io-mitmproxy-mitmproxy-4.0.1.index.json", 100 vrFName: "docker.io-mitmproxy-mitmproxy-4.0.1.report.json", 101 }, 102 } 103 104 for _, tt := range table { 105 // grab and deserialize test data 106 irPath := filepath.Join("testdata", tt.irFName) 107 vrPath := filepath.Join("testdata", tt.vrFName) 108 irFD, err := os.Open(irPath) 109 if err != nil { 110 t.Fatalf("fd open for ir failed: %v", err) 111 } 112 vrFD, err := os.Open(vrPath) 113 if err != nil { 114 t.Fatalf("fd open for vr failed: %v", err) 115 } 116 117 var ir claircore.IndexReport 118 var vr claircore.VulnerabilityReport 119 120 err = json.NewDecoder(irFD).Decode(&ir) 121 if err != nil { 122 t.Fatalf("could not decode ir: %v", err) 123 } 124 125 err = json.NewDecoder(vrFD).Decode(&vr) 126 if err != nil { 127 t.Fatalf("could not decode vr: %v", err) 128 } 129 130 // create and run e2e test 131 e2e := &affectedE2E{ 132 store: store, 133 pool: pool, 134 ctx: ctx, 135 ir: ir, 136 vr: vr, 137 } 138 t.Run(tt.name, e2e.Run) 139 } 140 } 141 142 func (e *affectedE2E) Run(t *testing.T) { 143 type subtest struct { 144 name string 145 do func(t *testing.T) 146 } 147 subtests := [...]subtest{ 148 {"IndexArtifacts", e.IndexArtifacts}, 149 {"IndexManifest", e.IndexManifest}, 150 {"AffectedManifests", e.AffectedManifests}, 151 } 152 for _, subtest := range subtests { 153 if !t.Run(subtest.name, subtest.do) { 154 t.FailNow() 155 } 156 } 157 } 158 159 // IndexArtifacts manually writes all the necessary 160 // artifacts to the db. 161 // 162 // this is required so foreign key constraints do not 163 // fail in later tests. 164 func (e *affectedE2E) IndexArtifacts(t *testing.T) { 165 ctx := zlog.Test(e.ctx, t) 166 const ( 167 insertManifest = ` 168 INSERT INTO manifest 169 (hash) 170 VALUES ($1) 171 ON CONFLICT DO NOTHING; 172 ` 173 insertPkg = ` 174 INSERT INTO package (name, kind, version, norm_kind, norm_version, module, arch, id) 175 VALUES ($1, $2, $3, $4, $5, $6, $7, $8) 176 ON CONFLICT DO NOTHING; 177 ` 178 insertDist = ` 179 INSERT INTO dist 180 (name, did, version, version_code_name, version_id, arch, cpe, pretty_name, id) 181 VALUES 182 ($1, $2, $3, $4, $5, $6, $7, $8, $9) 183 ON CONFLICT DO NOTHING; 184 ` 185 insertRepo = ` 186 INSERT INTO repo 187 (name, key, uri, id) 188 VALUES ($1, $2, $3, $4) 189 ON CONFLICT DO NOTHING; 190 ` 191 ) 192 _, err := e.pool.Exec(ctx, insertManifest, e.ir.Hash.String()) 193 if err != nil { 194 t.Fatalf("failed to insert manifest: %v", err) 195 } 196 for _, pkg := range e.ir.Packages { 197 var nVer pgtype.Int4Array 198 nVer.Status = pgtype.Present 199 nVer.Set(pkg.NormalizedVersion.V) 200 _, err := e.pool.Exec(ctx, insertPkg, 201 pkg.Name, 202 pkg.Kind, 203 pkg.Version, 204 pkg.NormalizedVersion.Kind, 205 &nVer, 206 pkg.Module, 207 pkg.Arch, 208 pkg.ID, 209 ) 210 if err != nil { 211 t.Fatalf("failed to insert package: %v", err) 212 } 213 if pkg.Source != nil { 214 pkg := pkg.Source 215 nVer.Set(pkg.NormalizedVersion.V) 216 _, err := e.pool.Exec(ctx, insertPkg, 217 pkg.Name, 218 pkg.Kind, 219 pkg.Version, 220 pkg.NormalizedVersion.Kind, 221 &nVer, 222 pkg.Module, 223 pkg.Arch, 224 pkg.ID, 225 ) 226 if err != nil { 227 t.Fatalf("failed to insert source package: %v", err) 228 } 229 } 230 } 231 for _, dist := range e.ir.Distributions { 232 _, err := e.pool.Exec(ctx, insertDist, 233 dist.Name, 234 dist.DID, 235 dist.Version, 236 dist.VersionCodeName, 237 dist.VersionID, 238 dist.Arch, 239 dist.CPE, 240 dist.PrettyName, 241 dist.ID, 242 ) 243 if err != nil { 244 t.Fatalf("failed to insert dist: %v", err) 245 } 246 } 247 for _, repo := range e.ir.Repositories { 248 _, err := e.pool.Exec(ctx, insertRepo, 249 repo.Name, 250 repo.Key, 251 repo.URI, 252 repo.ID, 253 ) 254 if err != nil { 255 t.Fatalf("failed to insert repo: %v", err) 256 } 257 } 258 } 259 260 // IndexManifest confirms the contents of a manifest 261 // can be written to the manifest index table. 262 func (e *affectedE2E) IndexManifest(t *testing.T) { 263 ctx := zlog.Test(e.ctx, t) 264 err := e.store.IndexManifest(ctx, &e.ir) 265 if err != nil { 266 t.Fatalf("failed to index manifest: %v", err) 267 } 268 } 269 270 // AffectedManifests confirms each vulnerability 271 // in the vulnereability report reports the associated 272 // manifest is affected. 273 func (e *affectedE2E) AffectedManifests(t *testing.T) { 274 ctx := zlog.Test(e.ctx, t) 275 om := omnimatcher.New(nil) 276 for _, vuln := range e.vr.Vulnerabilities { 277 hashes, err := e.store.AffectedManifests(ctx, *vuln, om.Vulnerable) 278 if err != nil { 279 t.Fatalf("failed to retrieve affected manifest for vuln %s: %v", vuln.ID, err) 280 } 281 282 if len(hashes) != 1 { 283 t.Fatalf("got: len(hashes)==%d, want: len(hashes)==1", len(hashes)) 284 } 285 286 got := hashes[0].String() 287 wanted := e.ir.Hash.String() 288 if got != wanted { 289 t.Fatalf("got: %v, want: %v", got, wanted) 290 } 291 } 292 }