github.com/quay/claircore@v1.5.28/updater/osv/osv_test.go (about)

     1  package osv
     2  
     3  import (
     4  	"archive/zip"
     5  	"bufio"
     6  	"bytes"
     7  	"context"
     8  	"encoding/json"
     9  	"errors"
    10  	"fmt"
    11  	"io"
    12  	"io/fs"
    13  	"net/http"
    14  	"net/http/httptest"
    15  	"os"
    16  	"path"
    17  	"path/filepath"
    18  	"strings"
    19  	"testing"
    20  
    21  	"github.com/quay/zlog"
    22  
    23  	"github.com/quay/claircore"
    24  	"github.com/quay/claircore/libvuln/driver"
    25  )
    26  
    27  func TestFetch(t *testing.T) {
    28  	srv := httptest.NewServer(&apiStub{t, ""})
    29  	defer srv.Close()
    30  	ctx := zlog.Test(context.Background(), t)
    31  
    32  	f := Factory{}
    33  	cfgFunc := func(v interface{}) error {
    34  		cfg := v.(*FactoryConfig)
    35  		cfg.URL = srv.URL
    36  		return nil
    37  	}
    38  	if err := f.Configure(ctx, cfgFunc, srv.Client()); err != nil {
    39  		t.Error(err)
    40  	}
    41  
    42  	s, err := f.UpdaterSet(ctx)
    43  	if err != nil {
    44  		t.Error(err)
    45  	}
    46  	if len(s.Updaters()) == 0 {
    47  		t.Errorf("expected more than 0 updaters")
    48  	}
    49  
    50  	for _, u := range s.Updaters() {
    51  		rc, fp, err := u.Fetch(ctx, driver.Fingerprint(""))
    52  		if err != nil {
    53  			t.Error(err)
    54  		}
    55  		_ = fp
    56  		if rc != nil {
    57  			rc.Close()
    58  		}
    59  
    60  	}
    61  }
    62  
    63  type apiStub struct {
    64  	*testing.T
    65  	path string
    66  }
    67  
    68  func (a *apiStub) ServeHTTP(w http.ResponseWriter, r *http.Request) {
    69  	a.Logf("req: %s", r.RequestURI)
    70  	sys := os.DirFS(filepath.Join("testdata", a.path))
    71  	p := r.URL.Path
    72  	switch {
    73  	case p == "/ecosystems.txt":
    74  		out := bufio.NewWriter(w)
    75  		defer out.Flush()
    76  		fmt.Fprintln(out, "testing_ecosystem")
    77  		ms, err := fs.Glob(sys, "*.zip")
    78  		if err != nil {
    79  			panic(err) // can only ever be ErrBadPatern
    80  		}
    81  		for _, m := range ms {
    82  			fmt.Fprintln(out, strings.TrimSuffix(m, ".zip"))
    83  		}
    84  	case strings.HasSuffix(p, "all.zip"):
    85  		w.WriteHeader(http.StatusOK)
    86  		n := strings.ToLower(path.Dir(p)[1:]) + ".zip"
    87  		a.Logf("serving %q", n)
    88  		if f, err := sys.Open(n); errors.Is(err, nil) {
    89  			defer f.Close()
    90  			if _, err := io.Copy(w, f); err != nil {
    91  				a.Error(err)
    92  			}
    93  			return
    94  		}
    95  		z := zip.NewWriter(w)
    96  		if err := z.SetComment("empty zip"); err != nil {
    97  			a.Error(err)
    98  		}
    99  		if err := z.Close(); err != nil {
   100  			a.Error(err)
   101  		}
   102  	default:
   103  		w.WriteHeader(http.StatusNotFound)
   104  	}
   105  }
   106  
   107  func TestParse(t *testing.T) {
   108  	srv := httptest.NewServer(&apiStub{t, ""})
   109  	defer srv.Close()
   110  	ctx := zlog.Test(context.Background(), t)
   111  
   112  	f := Factory{}
   113  	cfgFunc := func(v interface{}) error {
   114  		cfg := v.(*FactoryConfig)
   115  		cfg.URL = srv.URL
   116  		return nil
   117  	}
   118  	if err := f.Configure(ctx, cfgFunc, srv.Client()); err != nil {
   119  		t.Error(err)
   120  	}
   121  	s, err := f.UpdaterSet(ctx)
   122  	if err != nil {
   123  		t.Error(err)
   124  	}
   125  	if len(s.Updaters()) == 0 {
   126  		t.Errorf("expected more than 0 updaters")
   127  	}
   128  
   129  	for _, u := range s.Updaters() {
   130  		rc, _, err := u.Fetch(ctx, driver.Fingerprint(""))
   131  		if err != nil {
   132  			t.Error(err)
   133  		}
   134  		defer rc.Close()
   135  		vs, err := u.Parse(ctx, rc)
   136  		if err != nil {
   137  			t.Error(err)
   138  		}
   139  		t.Logf("parsed %d vulnerabilities", len(vs))
   140  		if len(vs) != 0 {
   141  			t.Log("first one:")
   142  			var buf bytes.Buffer
   143  			enc := json.NewEncoder(&buf)
   144  			enc.SetIndent("", "\t")
   145  			if err := enc.Encode(vs[0]); err != nil {
   146  				t.Error(err)
   147  			}
   148  			t.Log(buf.String())
   149  		}
   150  	}
   151  }
   152  
   153  var severityTestCases = []struct {
   154  	name                       string
   155  	a                          *advisory
   156  	expectedNormalizedSeverity claircore.Severity
   157  	expectedSeverity           string
   158  }{
   159  	{
   160  		name: "CVSS V3 HIGH",
   161  		a: &advisory{
   162  			ID: "test1",
   163  			Severity: []severity{
   164  				{
   165  					Type:  "CVSS_V3",
   166  					Score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
   167  				},
   168  			},
   169  			Affected: []affected{
   170  				{
   171  					Package: _package{
   172  						Ecosystem: "go",
   173  						Name:      "something",
   174  					},
   175  					Ranges: []_range{
   176  						{
   177  							Type: "ECOSYSTEM",
   178  							Events: []rangeEvent{
   179  								{
   180  									Introduced: "0.1",
   181  									Fixed:      "0.4",
   182  								},
   183  							},
   184  						},
   185  					},
   186  				},
   187  			},
   188  		},
   189  		expectedNormalizedSeverity: claircore.High,
   190  		expectedSeverity:           "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
   191  	},
   192  	{
   193  		name: "CVSS V2 MEDIUM",
   194  		a: &advisory{
   195  			ID: "test2",
   196  			Severity: []severity{
   197  				{
   198  					Type:  "CVSS_V2",
   199  					Score: "AV:L/AC:H/Au:N/C:C/I:C/A:C",
   200  				},
   201  			},
   202  			Affected: []affected{
   203  				{
   204  					Package: _package{
   205  						Ecosystem: "go",
   206  						Name:      "something",
   207  					},
   208  					Ranges: []_range{
   209  						{
   210  							Type: "ECOSYSTEM",
   211  							Events: []rangeEvent{
   212  								{
   213  									Introduced: "0.1",
   214  									Fixed:      "0.4",
   215  								},
   216  							},
   217  						},
   218  					},
   219  				},
   220  			},
   221  		},
   222  		expectedNormalizedSeverity: claircore.Medium,
   223  		expectedSeverity:           "AV:L/AC:H/Au:N/C:C/I:C/A:C",
   224  	},
   225  	{
   226  		name: "database_specific moderate",
   227  		a: &advisory{
   228  			ID: "test2",
   229  			Affected: []affected{
   230  				{
   231  					Package: _package{
   232  						Ecosystem: "go",
   233  						Name:      "something",
   234  					},
   235  					Ranges: []_range{
   236  						{
   237  							Type: "ECOSYSTEM",
   238  							Events: []rangeEvent{
   239  								{
   240  									Introduced: "0.1",
   241  									Fixed:      "0.4",
   242  								},
   243  							},
   244  						},
   245  					},
   246  				},
   247  			},
   248  			Database: json.RawMessage([]byte(`{"severity":"moderate"}`)),
   249  		},
   250  		expectedNormalizedSeverity: claircore.Medium,
   251  		expectedSeverity:           "moderate",
   252  	},
   253  	{
   254  		name: "CVSS V3 HIGH and database_specific moderate",
   255  		a: &advisory{
   256  			ID: "test2",
   257  			Severity: []severity{
   258  				{
   259  					Type:  "CVSS_V3",
   260  					Score: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
   261  				},
   262  			},
   263  			Affected: []affected{
   264  				{
   265  					Package: _package{
   266  						Ecosystem: "go",
   267  						Name:      "something",
   268  					},
   269  					Ranges: []_range{
   270  						{
   271  							Type: "ECOSYSTEM",
   272  							Events: []rangeEvent{
   273  								{
   274  									Introduced: "0.1",
   275  									Fixed:      "0.4",
   276  								},
   277  							},
   278  						},
   279  					},
   280  				},
   281  			},
   282  			Database: json.RawMessage([]byte(`{"severity":"moderate"}`)),
   283  		},
   284  		expectedNormalizedSeverity: claircore.High,
   285  		expectedSeverity:           "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
   286  	},
   287  }
   288  
   289  func TestSeverityParsing(t *testing.T) {
   290  	ctx := zlog.Test(context.Background(), t)
   291  
   292  	for _, tt := range severityTestCases {
   293  		t.Run(tt.name, func(t *testing.T) {
   294  			ecs := newECS("test")
   295  
   296  			err := ecs.Insert(ctx, nil, "", tt.a)
   297  			if err != nil {
   298  				t.Error("got error Inserting advisory", err)
   299  			}
   300  			if len(ecs.Vulnerability) != 1 {
   301  				t.Errorf("should have one vulnerability but got %d", len(ecs.Vulnerability))
   302  			}
   303  			v := ecs.Vulnerability[0]
   304  			if v.NormalizedSeverity != tt.expectedNormalizedSeverity {
   305  				t.Errorf("expected severity %q but got %q", tt.expectedNormalizedSeverity, v.NormalizedSeverity)
   306  			}
   307  			if v.Severity != tt.expectedSeverity {
   308  				t.Errorf("expected severity %q but got %q", tt.expectedSeverity, v.Severity)
   309  			}
   310  
   311  		})
   312  	}
   313  }