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  }