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  }