diff --git a/archop.go b/archop.go new file mode 100644 index 000000000..4ea10cace --- /dev/null +++ b/archop.go @@ -0,0 +1,85 @@ +package claircore + +import ( + "bytes" + "database/sql/driver" + "fmt" + "regexp" +) + +type ArchOp uint + +const ( + opInvalid ArchOp = iota // invalid + + OpEquals // equals + OpNotEquals // not equals + OpPatternMatch // pattern match +) + +func (o ArchOp) Cmp(a, b string) bool { + switch { + case b == "": + return true + case a == "": + return false + default: + } + switch o { + case OpEquals: + return a == b + case OpNotEquals: + return a != b + case OpPatternMatch: + re, err := regexp.Compile(b) + if err != nil { + return false + } + return re.MatchString(a) + default: + } + return false +} + +//go:generate stringer -type=ArchOp -linecomment + +func (o ArchOp) MarshalText() (text []byte, err error) { + return []byte(o.String()), nil +} + +func (o *ArchOp) UnmarshalText(text []byte) error { + i := bytes.Index([]byte(_ArchOp_name), text) + if i == -1 { + *o = ArchOp(0) + return nil + } + idx := uint8(i) + for i, off := range _ArchOp_index { + if off == idx { + *o = ArchOp(i) + return nil + } + } + panic("unreachable") +} + +func (o ArchOp) Value() (driver.Value, error) { + return o.String(), nil +} + +func (o *ArchOp) Scan(i interface{}) error { + switch v := i.(type) { + case []byte: + return o.UnmarshalText(v) + case string: + return o.UnmarshalText([]byte(v)) + case int64: + if v >= int64(len(_ArchOp_index)-1) { + return fmt.Errorf("unable to scan ArchOp from enum %d", v) + } + *o = ArchOp(v) + default: + return fmt.Errorf("unable to scan ArchOp from type %T", i) + } + return nil +} diff --git a/archop_string.go b/archop_string.go index 24575bfab..a054b85e8 100644 --- a/archop_string.go +++ b/archop_string.go @@ -8,18 +8,19 @@ func _() { // An "invalid array index" compiler error signifies that the constant values have changed. // Re-run the stringer command to generate them again. var x [1]struct{} + _ = x[opInvalid-0] _ = x[OpEquals-1] _ = x[OpNotEquals-2] + _ = x[OpPatternMatch-3] } -const _ArchOp_name = "equalsnot equals" +const _ArchOp_name = "invalidequalsnot equalspattern match" -var _ArchOp_index = [...]uint8{0, 6, 16} +var _ArchOp_index = [...]uint8{0, 7, 13, 23, 36} func (i ArchOp) String() string { - i -= 1 if i >= ArchOp(len(_ArchOp_index)-1) { - return "ArchOp(" + strconv.FormatInt(int64(i+1), 10) + ")" + return "ArchOp(" + strconv.FormatInt(int64(i), 10) + ")" } return _ArchOp_name[_ArchOp_index[i]:_ArchOp_index[i+1]] } diff --git a/internal/vulnstore/postgres/update_e2e_test.go b/internal/vulnstore/postgres/update_e2e_test.go index 6360a91af..08edb4d72 100644 --- a/internal/vulnstore/postgres/update_e2e_test.go +++ b/internal/vulnstore/postgres/update_e2e_test.go @@ -292,7 +292,7 @@ func (e *e2e) DeleteUpdateOperations(ctx context.Context) func(*testing.T) { } // checkInsertedVulns confirms vulnerabilitiles are inserted into the database correctly when -// store.UpdateVulnerabilities is calld. +// store.UpdateVulnerabilities is called. func checkInsertedVulns(ctx context.Context, t *testing.T, db *sqlx.DB, id uuid.UUID, vulns []*claircore.Vulnerability) { const query = `SELECT vuln.hash_kind, diff --git a/libvuln/migrations/migration1.go b/libvuln/migrations/migration1.go index 0c3fc40dd..64564d5aa 100644 --- a/libvuln/migrations/migration1.go +++ b/libvuln/migrations/migration1.go @@ -70,7 +70,7 @@ const ( repo_key TEXT, repo_uri TEXT, fixed_in_version TEXT, - arch_operation bigint, + arch_operation TEXT, vulnerable_range VersionRange NOT NULL DEFAULT VersionRange('{}', '{}', '()'), version_kind TEXT, UNIQUE (hash_kind, hash) diff --git a/osrelease/scanner_test.go b/osrelease/scanner_test.go index 096c8ed21..9c5221464 100644 --- a/osrelease/scanner_test.go +++ b/osrelease/scanner_test.go @@ -1,11 +1,7 @@ package osrelease import ( - "compress/gzip" "context" - "errors" - "io" - "net/http" "os" "path/filepath" "runtime/trace" @@ -15,7 +11,7 @@ import ( "github.com/quay/claircore" "github.com/quay/claircore/pkg/cpe" - "github.com/quay/claircore/test/integration" + "github.com/quay/claircore/test/fetch" "github.com/quay/claircore/test/log" ) @@ -135,50 +131,13 @@ func TestParse(t *testing.T) { } type layercase struct { - Name string - URL string - Want []*claircore.Distribution + Name string + Layer layerspec + Want []*claircore.Distribution } - -func (lc *layercase) name() string { - return filepath.Join("testdata", lc.Name+".layer") -} - -func (lc layercase) Prep(t *testing.T) { - t.Helper() - fn := lc.name() - _, err := os.Stat(fn) - switch { - case err == nil: - t.Logf("found layer cached: %q", fn) - return - case errors.Is(err, os.ErrNotExist): - integration.Skip(t) - t.Logf("fetching %q → %q", lc.URL, fn) - default: - t.Fatal(err) - } - f, err := os.Create(fn) - if err != nil { - t.Fatal(err) - } - defer f.Close() - res, err := http.Get(lc.URL) - if err != nil { - t.Fatal(err) - } - if res.StatusCode != http.StatusOK { - t.Fatal(res.Status) - } - rd, err := gzip.NewReader(res.Body) - if err != nil { - t.Fatal(err) - } - defer rd.Close() - if _, err := io.Copy(f, rd); err != nil { - t.Fatal(err) - } - t.Logf("fetched %q", fn) +type layerspec struct { + From, Repo string + Blob claircore.Digest } func (lc layercase) Test(t *testing.T) { @@ -188,7 +147,12 @@ func (lc layercase) Test(t *testing.T) { ctx = log.TestLogger(ctx, t) s := Scanner{} l := &claircore.Layer{} - if err := l.SetLocal(lc.name()); err != nil { + f, err := fetch.Layer(ctx, t, nil, lc.Layer.From, lc.Layer.Repo, lc.Layer.Blob) + if err != nil { + t.Fatal(err) + } + defer f.Close() + if err := l.SetLocal(f.Name()); err != nil { t.Fatal(err) } ds, err := s.Scan(ctx, l) @@ -205,7 +169,11 @@ func TestLayer(t *testing.T) { tt := []layercase{ { Name: "ubuntu_18.04", - URL: "https://storage.googleapis.com/quay-sandbox-01/datastorage/registry/sha256/35/35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a", + Layer: layerspec{ + From: "docker.io", + Repo: "library/ubuntu", + Blob: claircore.MustParseDigest(`sha256:35c102085707f703de2d9eaad8752d6fe1b8f02b5d2149f1d8357c9cc7fb7d0a`), + }, Want: []*claircore.Distribution{ &claircore.Distribution{ DID: "ubuntu", @@ -219,10 +187,6 @@ func TestLayer(t *testing.T) { }, } - for _, tc := range tt { - tc.Prep(t) - } - for _, tc := range tt { t.Run(tc.Name, tc.Test) } diff --git a/pkg/ovalutil/rpm.go b/pkg/ovalutil/rpm.go index 0bd735eb0..100cb7f97 100644 --- a/pkg/ovalutil/rpm.go +++ b/pkg/ovalutil/rpm.go @@ -119,6 +119,8 @@ func mapArchOp(op oval.Operation) claircore.ArchOp { return claircore.OpEquals case oval.OpNotEquals: return claircore.OpNotEquals + case oval.OpPatternMatch: + return claircore.OpPatternMatch default: } return claircore.ArchOp(0) diff --git a/vulnerability.go b/vulnerability.go index 4c294e49b..6a8819282 100644 --- a/vulnerability.go +++ b/vulnerability.go @@ -1,36 +1,9 @@ package claircore -import "time" - -type ArchOp uint - -const ( - _ ArchOp = iota // invalid - - OpEquals // equals - OpNotEquals // not equals +import ( + "time" ) -func (o ArchOp) Cmp(a, b string) bool { - switch { - case b == "": - return true - case a == "": - return false - default: - } - switch o { - case OpEquals: - return a == b - case OpNotEquals: - return a != b - default: - } - return false -} - -//go:generate stringer -type=ArchOp -linecomment - type Vulnerability struct { // unique ID of this vulnerability. this will be created as discovered by the library // and used for persistence and hash map indexes