Repository: anchore/grype
Pull request #1657 (2024-01-14 13:52:26)

fix: take VEX docs into account when --fail-on is set
Previously, VEX documents provided to Grype when --fail-on was set were not
taken into account. That led to inconsistent behaviour where a vulnerability
would be ignored when only `--vex` was specified, but would be included in
Grype output when both `--vex` and `--fail-on` were specified.

This change fixes that by moving the failure severity check to after the VEX
documents provided are tested.

I have also added a unit test to check that the combination of VEX docs and
failure severity checks works as expected.

I also took the opportunity to fix some typos while I was poking around.

fixes https://github.com/anchore/grype/issues/1639


Patch:
diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml
index 508c2e8c..b7b4ceaa 100644
--- a/.github/workflows/codeql-analysis.yml
+++ b/.github/workflows/codeql-analysis.yml
@@ -46,7 +46,7 @@ jobs:
       uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
 
     - name: Utilize Go Module Cache
-      uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 # v3.3.2
+      uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 # v4.0.0
       with:
         path: |
           ~/go/pkg/mod
diff --git a/.github/workflows/dependabot-automation.yaml b/.github/workflows/dependabot-automation.yaml
new file mode 100644
index 00000000..1c3ded29
--- /dev/null
+++ b/.github/workflows/dependabot-automation.yaml
@@ -0,0 +1,10 @@
+name: Dependabot Automation
+on:
+  pull_request:
+
+permissions:
+  pull-requests: write
+
+jobs:
+  run:
+    uses: anchore/workflows/.github/workflows/dependabot-automation.yaml@main
diff --git a/.github/workflows/release.yaml b/.github/workflows/release.yaml
index 2377a28b..d813b499 100644
--- a/.github/workflows/release.yaml
+++ b/.github/workflows/release.yaml
@@ -147,7 +147,7 @@ jobs:
           AWS_SECRET_ACCESS_KEY: ${{ secrets.TOOLBOX_AWS_SECRET_ACCESS_KEY }}
 
 
-      - uses: anchore/sbom-action@c7f031d9249a826a082ea14c79d3b686a51d485a # v0.15.3
+      - uses: anchore/sbom-action@24b0d5238516480139aa8bc6f92eeb7b54a9eb0a # v0.15.5
         continue-on-error: true
         with:
           artifact-name: sbom.spdx.json
diff --git a/.github/workflows/validations.yaml b/.github/workflows/validations.yaml
index 04352724..08fea28a 100644
--- a/.github/workflows/validations.yaml
+++ b/.github/workflows/validations.yaml
@@ -60,7 +60,7 @@ jobs:
 
       - name: Upload the provider state archive
         if: ${{ failure() }}
-        uses: actions/upload-artifact@c7d193f32edcb7bfad88892161225aeda64e9392 # v4.0.0
+        uses: actions/upload-artifact@694cdabd8bdb0f10b2cea11669e1bf5453eed0a6 # v4.2.0
         with:
           name: qg-capture-state
           path: qg-capture-state.tar.gz
@@ -106,7 +106,7 @@ jobs:
         run: make validate-cyclonedx-schema
 
       - name: Restore integration test cache
-        uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
+        uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
         with:
           path: ${{ github.workspace }}/test/integration/test-fixtures/cache
           key: ${{ runner.os }}-integration-test-cache-${{ hashFiles('test/integration/test-fixtures/cache.fingerprint') }}
@@ -137,7 +137,7 @@ jobs:
       # why not use actions/upload-artifact? It is very slow (3 minutes to upload ~600MB of data, vs 10 seconds with this approach).
       # see https://github.com/actions/upload-artifact/issues/199 for more info
       - name: Upload snapshot artifacts
-        uses: actions/cache/save@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
+        uses: actions/cache/save@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
         with:
           path: snapshot
           key: snapshot-build-${{ github.run_id }}
@@ -151,14 +151,14 @@ jobs:
       - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
 
       - name: Download snapshot build
-        uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
+        uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
         with:
           path: snapshot
           key: snapshot-build-${{ github.run_id }}
 
       - name: Restore install.sh test image cache
         id: install-test-image-cache
-        uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
+        uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
         with:
           path: ${{ github.workspace }}/test/install/cache
           key: ${{ runner.os }}-install-test-image-cache-${{ hashFiles('test/install/cache.fingerprint') }}
@@ -183,14 +183,14 @@ jobs:
       - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 #v4.1.1
 
       - name: Download snapshot build
-        uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
+        uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
         with:
           path: snapshot
           key: snapshot-build-${{ github.run_id }}
 
       - name: Restore docker image cache for compare testing
         id: mac-compare-testing-cache
-        uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
+        uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
         with:
           path: image.tar
           key: ${{ runner.os }}-${{ hashFiles('test/compare/mac.sh') }}
@@ -211,13 +211,13 @@ jobs:
         uses: ./.github/actions/bootstrap
 
       - name: Restore CLI test-fixture cache
-        uses: actions/cache@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
+        uses: actions/cache@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
         with:
           path: ${{ github.workspace }}/test/cli/test-fixtures/cache
           key: ${{ runner.os }}-cli-test-cache-${{ hashFiles('test/cli/test-fixtures/cache.fingerprint') }}
 
       - name: Download snapshot build
-        uses: actions/cache/restore@704facf57e6136b1bc63b828d79edcd491f0ee84 #v3.3.2
+        uses: actions/cache/restore@13aacd865c20de90d75de3b17ebe84f7a17d57d2 #v4.0.0
         with:
           path: snapshot
           key: snapshot-build-${{ github.run_id }}
diff --git a/cmd/grype/cli/commands/root.go b/cmd/grype/cli/commands/root.go
index 52212a96..dc1664dc 100644
--- a/cmd/grype/cli/commands/root.go
+++ b/cmd/grype/cli/commands/root.go
@@ -34,9 +34,9 @@ import (
 	"github.com/anchore/grype/internal/format"
 	"github.com/anchore/grype/internal/log"
 	"github.com/anchore/grype/internal/stringutil"
+	"github.com/anchore/syft/syft"
 	"github.com/anchore/syft/syft/linux"
 	syftPkg "github.com/anchore/syft/syft/pkg"
-	"github.com/anchore/syft/syft/pkg/cataloger"
 	"github.com/anchore/syft/syft/sbom"
 )
 
@@ -175,7 +175,7 @@ func runGrype(app clio.Application, opts *options.Grype, userInput string) (errs
 		Store:          *str,
 		IgnoreRules:    opts.Ignore,
 		NormalizeByCVE: opts.ByCVE,
-		FailSeverity:   opts.FailOnServerity(),
+		FailSeverity:   opts.FailOnSeverity(),
 		Matchers:       getMatchers(opts),
 		VexProcessor: vex.NewProcessor(vex.ProcessorOptions{
 			Documents:   opts.VexDocuments,
@@ -290,14 +290,15 @@ func getMatchers(opts *options.Grype) []matcher.Matcher {
 }
 
 func getProviderConfig(opts *options.Grype) pkg.ProviderConfig {
-	cfg := cataloger.DefaultConfig()
-	cfg.Search = opts.Search.ToConfig()
+	cfg := syft.DefaultCreateSBOMConfig()
+	cfg.Packages.JavaArchive.IncludeIndexedArchives = opts.Search.IncludeIndexedArchives
+	cfg.Packages.JavaArchive.IncludeUnindexedArchives = opts.Search.IncludeUnindexedArchives
 
 	return pkg.ProviderConfig{
 		SyftProviderConfig: pkg.SyftProviderConfig{
 			RegistryOptions:        opts.Registry.ToOptions(),
 			Exclusions:             opts.Exclusions,
-			CatalogingOptions:      cfg,
+			SBOMOptions:            cfg,
 			Platform:               opts.Platform,
 			Name:                   opts.Name,
 			DefaultImagePullSource: opts.DefaultImagePullSource,
diff --git a/cmd/grype/cli/options/grype.go b/cmd/grype/cli/options/grype.go
index 8219d77c..2b408c22 100644
--- a/cmd/grype/cli/options/grype.go
+++ b/cmd/grype/cli/options/grype.go
@@ -137,7 +137,7 @@ func (o *Grype) AddFlags(flags clio.FlagSet) {
 
 func (o *Grype) PostLoad() error {
 	if o.FailOn != "" {
-		failOnSeverity := *o.FailOnServerity()
+		failOnSeverity := *o.FailOnSeverity()
 		if failOnSeverity == vulnerability.UnknownSeverity {
 			return fmt.Errorf("bad --fail-on severity value '%s'", o.FailOn)
 		}
@@ -145,7 +145,7 @@ func (o *Grype) PostLoad() error {
 	return nil
 }
 
-func (o Grype) FailOnServerity() *vulnerability.Severity {
+func (o Grype) FailOnSeverity() *vulnerability.Severity {
 	severity := vulnerability.ParseSeverity(o.FailOn)
 	return &severity
 }
diff --git a/cmd/grype/cli/options/search.go b/cmd/grype/cli/options/search.go
index e1c59e17..2e812d2a 100644
--- a/cmd/grype/cli/options/search.go
+++ b/cmd/grype/cli/options/search.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 
 	"github.com/anchore/clio"
-	"github.com/anchore/syft/syft/pkg/cataloger"
+	"github.com/anchore/syft/syft/cataloging"
 	"github.com/anchore/syft/syft/source"
 )
 
@@ -17,7 +17,7 @@ type search struct {
 var _ clio.PostLoader = (*search)(nil)
 
 func defaultSearch(scope source.Scope) search {
-	c := cataloger.DefaultSearchConfig()
+	c := cataloging.DefaultArchiveSearchConfig()
 	return search{
 		Scope:                    scope.String(),
 		IncludeUnindexedArchives: c.IncludeUnindexedArchives,
@@ -36,11 +36,3 @@ func (cfg *search) PostLoad() error {
 func (cfg search) GetScope() source.Scope {
 	return source.ParseScope(cfg.Scope)
 }
-
-func (cfg search) ToConfig() cataloger.SearchConfig {
-	return cataloger.SearchConfig{
-		IncludeIndexedArchives:   cfg.IncludeIndexedArchives,
-		IncludeUnindexedArchives: cfg.IncludeUnindexedArchives,
-		Scope:                    cfg.GetScope(),
-	}
-}
diff --git a/cmd/grype/internal/ui/ui.go b/cmd/grype/internal/ui/ui.go
index 4ca88e73..48afdc33 100644
--- a/cmd/grype/internal/ui/ui.go
+++ b/cmd/grype/internal/ui/ui.go
@@ -151,8 +151,11 @@ func (m *UI) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			return m, nil
 		}
 
-		newModels, _ := m.handler.Handle(msg)
-		for _, newModel := range newModels {
+		models, cmd := m.handler.Handle(msg)
+		if cmd != nil {
+			cmds = append(cmds, cmd)
+		}
+		for _, newModel := range models {
 			if newModel == nil {
 				continue
 			}
diff --git a/go.mod b/go.mod
index e522ff26..fd857670 100644
--- a/go.mod
+++ b/go.mod
@@ -13,13 +13,13 @@ require (
 	github.com/anchore/go-testutils v0.0.0-20200925183923-d5f45b0d3c04
 	github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4
 	github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501
-	github.com/anchore/stereoscope v0.0.0-20231220161148-590920dabc54
-	github.com/anchore/syft v0.100.0
+	github.com/anchore/stereoscope v0.0.0-20240118133533-eb656fc71793
+	github.com/anchore/syft v0.101.1
 	github.com/aquasecurity/go-pep440-version v0.0.0-20210121094942-22b2f8951d46
 	github.com/bmatcuk/doublestar/v2 v2.0.4
 	github.com/charmbracelet/bubbletea v0.25.0
 	github.com/charmbracelet/lipgloss v0.9.1
-	github.com/docker/docker v24.0.7+incompatible
+	github.com/docker/docker v25.0.0+incompatible
 	github.com/dustin/go-humanize v1.0.1
 	github.com/facebookincubator/nvdtools v0.1.5
 	github.com/gabriel-vasile/mimetype v1.4.3
@@ -27,7 +27,7 @@ require (
 	github.com/glebarez/sqlite v1.10.0
 	github.com/go-test/deep v1.1.0
 	github.com/google/go-cmp v0.6.0
-	github.com/google/go-containerregistry v0.17.0
+	github.com/google/go-containerregistry v0.18.0
 	github.com/google/uuid v1.5.0
 	github.com/gookit/color v1.5.4
 	github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b
@@ -90,7 +90,7 @@ require (
 	github.com/becheran/wildmatch-go v1.0.0 // indirect
 	github.com/bgentry/go-netrc v0.0.0-20140422174119-9fd32a8b3d3d // indirect
 	github.com/bmatcuk/doublestar/v4 v4.6.1 // indirect
-	github.com/charmbracelet/bubbles v0.16.1 // indirect
+	github.com/charmbracelet/bubbles v0.17.1 // indirect
 	github.com/charmbracelet/harmonica v0.2.0 // indirect
 	github.com/cloudflare/circl v1.3.7 // indirect
 	github.com/containerd/cgroups v1.1.0 // indirect
@@ -235,13 +235,13 @@ require (
 	go.uber.org/atomic v1.9.0 // indirect
 	go.uber.org/goleak v1.2.0 // indirect
 	go.uber.org/multierr v1.9.0 // indirect
-	golang.org/x/crypto v0.17.0 // indirect
+	golang.org/x/crypto v0.18.0 // indirect
 	golang.org/x/mod v0.14.0 // indirect
-	golang.org/x/net v0.19.0 // indirect
+	golang.org/x/net v0.20.0 // indirect
 	golang.org/x/oauth2 v0.15.0 // indirect
 	golang.org/x/sync v0.5.0 // indirect
-	golang.org/x/sys v0.15.0 // indirect
-	golang.org/x/term v0.15.0 // indirect
+	golang.org/x/sys v0.16.0 // indirect
+	golang.org/x/term v0.16.0 // indirect
 	golang.org/x/text v0.14.0 // indirect
 	golang.org/x/time v0.5.0 // indirect
 	golang.org/x/tools v0.13.0 // indirect
diff --git a/go.sum b/go.sum
index 79e226d7..80f2e2a2 100644
--- a/go.sum
+++ b/go.sum
@@ -249,10 +249,10 @@ github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4 h1:rmZG77uXgE
 github.com/anchore/go-version v1.2.2-0.20210903204242-51efa5b487c4/go.mod h1:Bkc+JYWjMCF8OyZ340IMSIi2Ebf3uwByOk6ho4wne1E=
 github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501 h1:AV7qjwMcM4r8wFhJq3jLRztew3ywIyPTRapl2T1s9o8=
 github.com/anchore/packageurl-go v0.1.1-0.20230104203445-02e0a6721501/go.mod h1:Blo6OgJNiYF41ufcgHKkbCKF2MDOMlrqhXv/ij6ocR4=
-github.com/anchore/stereoscope v0.0.0-20231220161148-590920dabc54 h1:i2YK5QEs9H2YB3B2zv+AGR44ves0nmAGOD07lMphH14=
-github.com/anchore/stereoscope v0.0.0-20231220161148-590920dabc54/go.mod h1:IylG7ofLoUKHwS1XDF6rPhOmaE3GgpAgsMdvvYfooTU=
-github.com/anchore/syft v0.100.0 h1:XUpV4xWmD2cBS9hhhEdJEppItz0AxG8f5W3JhI2tQvY=
-github.com/anchore/syft v0.100.0/go.mod h1:laFRFA/okrA+ut+wPCU32hNkdPEwQfXyaB7E21ymWFc=
+github.com/anchore/stereoscope v0.0.0-20240118133533-eb656fc71793 h1:wji+qdjsV7ooolBwb3faVZnEK3WtY/kcT5473kxVZS4=
+github.com/anchore/stereoscope v0.0.0-20240118133533-eb656fc71793/go.mod h1:IylG7ofLoUKHwS1XDF6rPhOmaE3GgpAgsMdvvYfooTU=
+github.com/anchore/syft v0.101.1 h1:PTh7XBdtXq3BYhuPz67rrC6AFPZxC1Rt8jgqv7Z75rA=
+github.com/anchore/syft v0.101.1/go.mod h1:6rbrRWQN16TFENxXG1uFQOh9RCIp/UHJqPAJnHSKhjQ=
 github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883/go.mod h1:rCTlJbsFo29Kk6CurOXKm700vrz8f0KW0JNfpkRJY/8=
 github.com/andybalholm/brotli v1.0.1/go.mod h1:loMXtMfwqflxFJPmdbJO0a3KNoPuLBgiu3qAvBg8x/Y=
 github.com/andybalholm/brotli v1.0.4 h1:V7DdXeJtZscaqfNuAdSRuRFzuiKlHSC/Zh3zl9qY3JY=
@@ -273,6 +273,8 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
 github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
 github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs=
+github.com/atotto/clipboard v0.1.4 h1:EH0zSVneZPSuFR11BlR9YppQTVDbh5+16AmcJi4g1z4=
+github.com/atotto/clipboard v0.1.4/go.mod h1:ZY9tmq7sm5xIbd9bOK4onWV4S6X0u6GY7Vn0Yu86PYI=
 github.com/aws/aws-sdk-go v1.44.122/go.mod h1:y4AeaBuwd2Lk+GepC1E9v0qOiTws0MIWAX4oIKwKHZo=
 github.com/aws/aws-sdk-go v1.44.288 h1:Ln7fIao/nl0ACtelgR1I4AiEw/GLNkKcXfCaHupUW5Q=
 github.com/aws/aws-sdk-go v1.44.288/go.mod h1:aVsgQcEevwlmQ7qHE9I3h+dtQgpqhFB+i8Phjh7fkwI=
@@ -293,13 +295,15 @@ github.com/bmatcuk/doublestar/v4 v4.6.1/go.mod h1:xBQ8jztBU6kakFMg+8WGxn0c6z1fTS
 github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
 github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
 github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
+github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
+github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=
 github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
 github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
 github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
 github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
-github.com/charmbracelet/bubbles v0.16.1 h1:6uzpAAaT9ZqKssntbvZMlksWHruQLNxg49H5WdeuYSY=
-github.com/charmbracelet/bubbles v0.16.1/go.mod h1:2QCp9LFlEsBQMvIYERr7Ww2H2bA7xen1idUDIzm/+Xc=
+github.com/charmbracelet/bubbles v0.17.1 h1:0SIyjOnkrsfDo88YvPgAWvZMwXe26TP6drRvmkjyUu4=
+github.com/charmbracelet/bubbles v0.17.1/go.mod h1:9HxZWlkCqz2PRwsCbYl7a3KXvGzFaDHpYbSYMJ+nE3o=
 github.com/charmbracelet/bubbletea v0.25.0 h1:bAfwk7jRz7FKFl9RzlIULPkStffg5k6pNt5dywy4TcM=
 github.com/charmbracelet/bubbletea v0.25.0/go.mod h1:EN3QDR1T5ZdWmdfDzYcqOCAps45+QIJbLOBxmVNWNNg=
 github.com/charmbracelet/harmonica v0.2.0 h1:8NxJWRWg/bzKqqEaaeFNipOu77YR5t8aSwG4pgaUBiQ=
@@ -372,8 +376,8 @@ github.com/docker/cli v24.0.0+incompatible h1:0+1VshNwBQzQAx9lOl+OYCTCEAD8fKs/qe
 github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=
 github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=
-github.com/docker/docker v24.0.7+incompatible h1:Wo6l37AuwP3JaMnZa226lzVXGA3F9Ig1seQen0cKYlM=
-github.com/docker/docker v24.0.7+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
+github.com/docker/docker v25.0.0+incompatible h1:g9b6wZTblhMgzOT2tspESstfw6ySZ9kdm94BLDKaZac=
+github.com/docker/docker v25.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=
 github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A=
 github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0=
 github.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=
@@ -532,8 +536,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
 github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
 github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
 github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
-github.com/google/go-containerregistry v0.17.0 h1:5p+zYs/R4VGHkhyvgWurWrpJ2hW4Vv9fQI+GzdcwXLk=
-github.com/google/go-containerregistry v0.17.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
+github.com/google/go-containerregistry v0.18.0 h1:ShE7erKNPqRh5ue6Z9DUOlk04WsnFWPO6YGr3OxnfoQ=
+github.com/google/go-containerregistry v0.18.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ=
 github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
 github.com/google/licensecheck v0.3.1 h1:QoxgoDkaeC4nFrtGN1jV7IPmDCHFNIVh54e5hSt6sPs=
 github.com/google/licensecheck v0.3.1/go.mod h1:ORkR35t/JjW+emNKtfJDII0zlciG9JgbT7SmsohlHmY=
@@ -589,7 +593,10 @@ github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+
 github.com/gookit/color v1.2.5/go.mod h1:AhIE+pS6D4Ql0SQWbBeXPHw7gY0/sjHoA4s/n1KB7xg=
 github.com/gookit/color v1.5.4 h1:FZmqs7XOyGgCAxmWyPslpiok1k05wmY3SJTytgvYFs0=
 github.com/gookit/color v1.5.4/go.mod h1:pZJOeOS8DM43rXbp4AZo1n9zCU2qjpcRko0b6/QJi9w=
+github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo=
 github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
+github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg=
 github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b h1:wDUNC2eKiL35DbLvsDhiblTUXHxcOPwQSCzi7xpQUN4=
 github.com/hako/durafmt v0.0.0-20210608085754-5c1018a4e16b/go.mod h1:VzxiSdG6j1pi7rwGm/xYI5RbtpBgM8sARDXlvEvxlu0=
 github.com/hashicorp/consul/api v1.11.0/go.mod h1:XjsvQN+RJGWI2TWy1/kqaE16HrR2J/FWgkYjdZQsX9M=
@@ -889,6 +896,8 @@ github.com/sagikazarmark/locafero v0.3.0 h1:zT7VEGWC2DTflmccN/5T1etyKvxSxpHsjb9c
 github.com/sagikazarmark/locafero v0.3.0/go.mod h1:w+v7UsPNFwzF1cHuOajOOzoq4U7v/ig1mpRjqV+Bu1U=
 github.com/sagikazarmark/slog-shim v0.1.0 h1:diDBnUNK9N/354PgrxMywXnAwEr1QZcOr6gto+ugjYE=
 github.com/sagikazarmark/slog-shim v0.1.0/go.mod h1:SrcSrq8aKtyuqEI1uvTDTK1arOWRIczQRv+GVI1AkeQ=
+github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f h1:MvTmaQdww/z0Q4wrYjDSCcZ78NoftLQyHBSLW/Cx79Y=
+github.com/sahilm/fuzzy v0.1.1-0.20230530133925-c48e322e2a8f/go.mod h1:VFvziUEIMCrT6A6tw2RFIXPXXmzXbOsSHF0DOI8ZK9Y=
 github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d h1:hrujxIzL1woJ7AwssoOcM/tq5JjjG2yYOc8odClEiXA=
 github.com/saintfish/chardet v0.0.0-20230101081208-5e3ef4b5456d/go.mod h1:uugorj2VCxiV1x+LzaIdVa9b4S4qGAcH6cbhh4qVxOU=
 github.com/sanity-io/litter v1.5.5 h1:iE+sBxPBzoK6uaEP5Lt3fHNgpKcHXc/A2HGETy0uJQo=
@@ -1044,11 +1053,19 @@ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0 h1:x8Z78aZ
 go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.45.0/go.mod h1:62CPTSry9QZtOaSsE3tOzhx6LzDhHnXJ6xHeMNNiM6Q=
 go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs=
 go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0 h1:Mne5On7VWdx7omSrSSZvM4Kw7cS7NQkOOmLcgscI51U=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg=
+go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU=
 go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE=
 go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8=
+go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o=
+go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A=
 go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg=
 go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
+go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I=
+go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM=
 go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
 go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
 go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
@@ -1075,8 +1092,8 @@ golang.org/x/crypto v0.0.0-20220622213112-05595931fe9d/go.mod h1:IxCIyHEi3zRg3s0
 golang.org/x/crypto v0.3.0/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4=
 golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU=
-golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k=
-golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=
+golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
+golang.org/x/crypto v0.18.0/go.mod h1:R0j02AL6hcrfOiy9T4ZYp/rcWeMxM3L6QYxlOuEG1mg=
 golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
 golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
@@ -1179,8 +1196,8 @@ golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
 golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
 golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc=
-golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c=
-golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U=
+golang.org/x/net v0.20.0 h1:aCL9BSgETF1k+blQaYUBx9hJ9LOGP3gAVemcZlf1Kpo=
+golang.org/x/net v0.20.0/go.mod h1:z8BVo6PvndSri0LbOE3hAn0apkU+1YvI6E70E9jsnvY=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
 golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
@@ -1326,8 +1343,8 @@ golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=
-golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
+golang.org/x/sys v0.16.0 h1:xWw16ngr6ZMtmxDyKyIgsE93KNKz5HKmMa3b8ALHidU=
+golang.org/x/sys v0.16.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -1335,8 +1352,8 @@ golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
 golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
 golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U=
-golang.org/x/term v0.15.0 h1:y/Oo/a/q3IXu26lQgl04j/gjuBDOBlx7X6Om1j2CPW4=
-golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=
+golang.org/x/term v0.16.0 h1:m+B6fahuftsE9qjo0VWp2FW0mB3MTJvR0BaMQrq0pmE=
+golang.org/x/term v0.16.0/go.mod h1:yn7UURbUtPyrVJPGPq404EukNFxcm/foM+bV/bfcDsY=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
diff --git a/grype/deprecated.go b/grype/deprecated.go
index 5407d968..e9e4d4eb 100644
--- a/grype/deprecated.go
+++ b/grype/deprecated.go
@@ -7,8 +7,8 @@ import (
 	"github.com/anchore/grype/grype/store"
 	"github.com/anchore/grype/internal/log"
 	"github.com/anchore/stereoscope/pkg/image"
+	"github.com/anchore/syft/syft"
 	"github.com/anchore/syft/syft/linux"
-	"github.com/anchore/syft/syft/pkg/cataloger"
 	"github.com/anchore/syft/syft/source"
 )
 
@@ -16,11 +16,11 @@ import (
 func FindVulnerabilities(store store.Store, userImageStr string, scopeOpt source.Scope, registryOptions *image.RegistryOptions) (match.Matches, pkg.Context, []pkg.Package, error) {
 	providerConfig := pkg.ProviderConfig{
 		SyftProviderConfig: pkg.SyftProviderConfig{
-			RegistryOptions:   registryOptions,
-			CatalogingOptions: cataloger.DefaultConfig(),
+			RegistryOptions: registryOptions,
+			SBOMOptions:     syft.DefaultCreateSBOMConfig(),
 		},
 	}
-	providerConfig.CatalogingOptions.Search.Scope = scopeOpt
+	providerConfig.SBOMOptions.Search.Scope = scopeOpt
 
 	packages, context, _, err := pkg.Provide(userImageStr, providerConfig)
 	if err != nil {
diff --git a/grype/pkg/provider_config.go b/grype/pkg/provider_config.go
index 2fd8d97e..978f5da3 100644
--- a/grype/pkg/provider_config.go
+++ b/grype/pkg/provider_config.go
@@ -2,7 +2,7 @@ package pkg
 
 import (
 	"github.com/anchore/stereoscope/pkg/image"
-	"github.com/anchore/syft/syft/pkg/cataloger"
+	"github.com/anchore/syft/syft"
 )
 
 type ProviderConfig struct {
@@ -11,7 +11,7 @@ type ProviderConfig struct {
 }
 
 type SyftProviderConfig struct {
-	CatalogingOptions      cataloger.Config
+	SBOMOptions            *syft.CreateSBOMConfig
 	RegistryOptions        *image.RegistryOptions
 	Platform               string
 	Exclusions             []string
diff --git a/grype/pkg/syft_provider.go b/grype/pkg/syft_provider.go
index 0f1b725b..2f04da61 100644
--- a/grype/pkg/syft_provider.go
+++ b/grype/pkg/syft_provider.go
@@ -1,6 +1,9 @@
 package pkg
 
 import (
+	"context"
+	"errors"
+
 	"github.com/anchore/grype/internal/log"
 	"github.com/anchore/stereoscope/pkg/image"
 	"github.com/anchore/syft/syft"
@@ -22,34 +25,30 @@ func syftProvider(userInput string, config ProviderConfig) ([]Package, Context,
 		}
 	}()
 
-	catalog, relationships, theDistro, err := syft.CatalogPackages(src, config.CatalogingOptions)
+	s, err := syft.CreateSBOM(context.Background(), src, config.SBOMOptions)
 	if err != nil {
 		return nil, Context{}, nil, err
 	}
 
-	catalog = removePackagesByOverlap(catalog, relationships, theDistro)
+	if s == nil {
+		return nil, Context{}, nil, errors.New("no SBOM provided")
+	}
+
+	pkgCatalog := removePackagesByOverlap(s.Artifacts.Packages, s.Relationships, s.Artifacts.LinuxDistribution)
 
 	srcDescription := src.Describe()
 
-	packages := FromCollection(catalog, config.SynthesisConfig)
-	context := Context{
+	packages := FromCollection(pkgCatalog, config.SynthesisConfig)
+	pkgCtx := Context{
 		Source: &srcDescription,
-		Distro: theDistro,
-	}
-
-	sbom := &sbom.SBOM{
-		Source:        srcDescription,
-		Relationships: relationships,
-		Artifacts: sbom.Artifacts{
-			Packages: catalog,
-		},
+		Distro: s.Artifacts.LinuxDistribution,
 	}
 
-	return packages, context, sbom, nil
+	return packages, pkgCtx, s, nil
 }
 
 func getSource(userInput string, config ProviderConfig) (source.Source, error) {
-	if config.CatalogingOptions.Search.Scope == "" {
+	if config.SBOMOptions.Search.Scope == "" {
 		return nil, errDoesNotProvide
 	}
 
diff --git a/grype/vulnerability_matcher.go b/grype/vulnerability_matcher.go
index 14f5299e..7ae9dbf4 100644
--- a/grype/vulnerability_matcher.go
+++ b/grype/vulnerability_matcher.go
@@ -83,6 +83,11 @@ func (m *VulnerabilityMatcher) FindMatches(pkgs []pkg.Package, context pkg.Conte
 		return remainingMatches, ignoredMatches, err
 	}
 
+	if m.FailSeverity != nil && HasSeverityAtOrAbove(m.Store, *m.FailSeverity, *remainingMatches) {
+		err = grypeerr.ErrAboveSeverityThreshold
+		return remainingMatches, ignoredMatches, err
+	}
+
 	logListSummary(progressMonitor)
 
 	logIgnoredMatches(ignoredMatches)
@@ -114,10 +119,6 @@ func (m *VulnerabilityMatcher) findDBMatches(pkgs []pkg.Package, context pkg.Con
 		matches, ignoredMatches = m.applyIgnoreRules(normalizedMatches)
 	}
 
-	if m.FailSeverity != nil && HasSeverityAtOrAbove(m.Store, *m.FailSeverity, matches) {
-		err = grypeerr.ErrAboveSeverityThreshold
-	}
-
 	return &matches, ignoredMatches, err
 }
 


Test patch:
diff --git a/cmd/grype/cli/commands/root_test.go b/cmd/grype/cli/commands/root_test.go
index 953fa42c..82ed09b7 100644
--- a/cmd/grype/cli/commands/root_test.go
+++ b/cmd/grype/cli/commands/root_test.go
@@ -11,7 +11,7 @@ import (
 	"github.com/anchore/grype/cmd/grype/cli/options"
 	"github.com/anchore/grype/grype/pkg"
 	"github.com/anchore/stereoscope/pkg/image"
-	"github.com/anchore/syft/syft/pkg/cataloger"
+	"github.com/anchore/syft/syft"
 	"github.com/anchore/syft/syft/pkg/cataloger/binary"
 )
 
@@ -61,7 +61,7 @@ func Test_getProviderConfig(t *testing.T) {
 			}),
 			want: pkg.ProviderConfig{
 				SyftProviderConfig: pkg.SyftProviderConfig{
-					CatalogingOptions: cataloger.DefaultConfig(),
+					SBOMOptions: syft.DefaultCreateSBOMConfig(),
 					RegistryOptions: &image.RegistryOptions{
 						Credentials: []image.RegistryCredentials{},
 					},
@@ -71,8 +71,11 @@ func Test_getProviderConfig(t *testing.T) {
 	}
 	for _, tt := range tests {
 		t.Run(tt.name, func(t *testing.T) {
-			opts := cmpopts.IgnoreFields(binary.Classifier{}, "EvidenceMatcher")
-			if d := cmp.Diff(tt.want, getProviderConfig(tt.opts), opts); d != "" {
+			opts := cmp.Options{
+				cmpopts.IgnoreFields(binary.Classifier{}, "EvidenceMatcher"),
+				cmpopts.IgnoreUnexported(syft.CreateSBOMConfig{}),
+			}
+			if d := cmp.Diff(tt.want, getProviderConfig(tt.opts), opts...); d != "" {
 				t.Errorf("getProviderConfig() mismatch (-want +got):\n%s", d)
 			}
 		})
diff --git a/grype/pkg/provider_test.go b/grype/pkg/provider_test.go
index 34dd9443..9829a5e7 100644
--- a/grype/pkg/provider_test.go
+++ b/grype/pkg/provider_test.go
@@ -6,8 +6,8 @@ import (
 	"github.com/stretchr/testify/assert"
 
 	"github.com/anchore/stereoscope/pkg/imagetest"
+	"github.com/anchore/syft/syft"
 	"github.com/anchore/syft/syft/file"
-	"github.com/anchore/syft/syft/pkg/cataloger"
 )
 
 func TestProviderLocationExcludes(t *testing.T) {
@@ -47,8 +47,8 @@ func TestProviderLocationExcludes(t *testing.T) {
 		t.Run(test.name, func(t *testing.T) {
 			cfg := ProviderConfig{
 				SyftProviderConfig: SyftProviderConfig{
-					Exclusions:        test.excludes,
-					CatalogingOptions: cataloger.DefaultConfig(),
+					Exclusions:  test.excludes,
+					SBOMOptions: syft.DefaultCreateSBOMConfig(),
 				},
 			}
 			pkgs, _, _, _ := Provide(test.fixture, cfg)
@@ -102,8 +102,8 @@ func TestSyftLocationExcludes(t *testing.T) {
 			userInput := imagetest.GetFixtureImageTarPath(t, test.fixture)
 			cfg := ProviderConfig{
 				SyftProviderConfig: SyftProviderConfig{
-					Exclusions:        test.excludes,
-					CatalogingOptions: cataloger.DefaultConfig(),
+					Exclusions:  test.excludes,
+					SBOMOptions: syft.DefaultCreateSBOMConfig(),
 				},
 			}
 			pkgs, _, _, err := Provide(userInput, cfg)
diff --git a/grype/presenter/explain/test-fixtures/chainguard-ruby-test.json b/grype/presenter/explain/test-fixtures/chainguard-ruby-test.json
index 22151b2e..713d44db 100644
--- a/grype/presenter/explain/test-fixtures/chainguard-ruby-test.json
+++ b/grype/presenter/explain/test-fixtures/chainguard-ruby-test.json
@@ -346,7 +346,6 @@
    "distro": "",
    "add-cpes-if-none": false,
    "output-template-file": "",
-   "quiet": false,
    "check-for-app-update": true,
    "only-fixed": false,
    "only-notfixed": false,
@@ -408,7 +407,8 @@
     "auth": []
    },
    "log": {
-    "structured": false,
+    "quiet": false,
+    "verbosity": 0,
     "level": "warn",
     "file": ""
    },
diff --git a/grype/presenter/explain/test-fixtures/ghsa-test.json b/grype/presenter/explain/test-fixtures/ghsa-test.json
index 661b78b0..4eaed3c1 100644
--- a/grype/presenter/explain/test-fixtures/ghsa-test.json
+++ b/grype/presenter/explain/test-fixtures/ghsa-test.json
@@ -313,7 +313,6 @@
    "distro": "",
    "add-cpes-if-none": false,
    "output-template-file": "",
-   "quiet": true,
    "check-for-app-update": true,
    "only-fixed": false,
    "only-notfixed": false,
@@ -375,7 +374,8 @@
     "auth": []
    },
    "log": {
-    "structured": false,
+    "quiet": true,
+    "verbosity": 0,
     "level": "",
     "file": ""
    },
diff --git a/grype/presenter/explain/test-fixtures/keycloak-test.json b/grype/presenter/explain/test-fixtures/keycloak-test.json
index c14db8cb..55edfd02 100644
--- a/grype/presenter/explain/test-fixtures/keycloak-test.json
+++ b/grype/presenter/explain/test-fixtures/keycloak-test.json
@@ -755,7 +755,6 @@
    "distro": "",
    "add-cpes-if-none": false,
    "output-template-file": "",
-   "quiet": true,
    "check-for-app-update": true,
    "only-fixed": false,
    "only-notfixed": false,
@@ -818,7 +817,8 @@
     "ca-cert": ""
    },
    "log": {
-    "structured": false,
+    "quiet": true,
+    "verbosity": 0,
     "level": "",
     "file": ""
    },
diff --git a/grype/vex/processor_test.go b/grype/vex/processor_test.go
index 85168f2b..4a4d0a21 100644
--- a/grype/vex/processor_test.go
+++ b/grype/vex/processor_test.go
@@ -92,7 +92,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
 		return &s
 	}
 
-	metchesRef := func(ms ...match.Match) *match.Matches {
+	matchesRef := func(ms ...match.Match) *match.Matches {
 		m := match.NewMatches(ms...)
 		return &m
 	}
@@ -127,7 +127,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
 				pkgContext: pkgContext,
 				matches:    getSubject(),
 			},
-			wantMatches: metchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
+			wantMatches: matchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
 			wantIgnoredMatches: []match.IgnoredMatch{
 				{
 					Match: libCryptoCVE_2023_1255,
@@ -157,7 +157,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
 				pkgContext: pkgContext,
 				matches:    getSubject(),
 			},
-			wantMatches: metchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
+			wantMatches: matchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
 			wantIgnoredMatches: []match.IgnoredMatch{
 				{
 					Match: libCryptoCVE_2023_1255,
@@ -187,7 +187,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
 				pkgContext: pkgContext,
 				matches:    getSubject(),
 			},
-			wantMatches: metchesRef(libCryptoCVE_2023_3817),
+			wantMatches: matchesRef(libCryptoCVE_2023_3817),
 			wantIgnoredMatches: []match.IgnoredMatch{
 				{
 					Match: libCryptoCVE_2023_1255,
@@ -226,7 +226,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
 				pkgContext: pkgContext,
 				matches:    getSubject(),
 			},
-			wantMatches: metchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
+			wantMatches: matchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975),
 			wantIgnoredMatches: []match.IgnoredMatch{
 				{
 					Match: libCryptoCVE_2023_1255,
@@ -258,7 +258,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
 				matches:    getSubject(),
 			},
 			// nothing gets ignored!
-			wantMatches:        metchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975, libCryptoCVE_2023_1255),
+			wantMatches:        matchesRef(libCryptoCVE_2023_3817, libCryptoCVE_2023_2975, libCryptoCVE_2023_1255),
 			wantIgnoredMatches: []match.IgnoredMatch{},
 		},
 		{
@@ -278,7 +278,7 @@ func TestProcessor_ApplyVEX(t *testing.T) {
 				pkgContext: pkgContext,
 				matches:    getSubject(),
 			},
-			wantMatches: metchesRef(libCryptoCVE_2023_2975, libCryptoCVE_2023_1255),
+			wantMatches: matchesRef(libCryptoCVE_2023_2975, libCryptoCVE_2023_1255),
 			wantIgnoredMatches: []match.IgnoredMatch{
 				{
 					Match: libCryptoCVE_2023_3817,
diff --git a/grype/vex/testdata/vex-docs/openvex-debian.json b/grype/vex/testdata/vex-docs/openvex-debian.json
new file mode 100644
index 00000000..4a32e135
--- /dev/null
+++ b/grype/vex/testdata/vex-docs/openvex-debian.json
@@ -0,0 +1,20 @@
+{
+    "@context": "https://openvex.dev/ns/v0.2.0",
+    "@id": "https://openvex.dev/docs/public/vex-d4e9020b6d0d26f131d535e055902dd6ccf3e2088bce3079a8cd3588a4b14c78",
+    "author": "The OpenVEX Project <openvex@openssf.org>",
+    "timestamp": "2023-07-17T18:28:47.696004345-06:00",
+    "version": 1,
+    "statements": [
+      {
+        "vulnerability": {
+          "name": "CVE-2014-fake-1"
+        },
+        "products": [
+          {
+            "@id": "pkg:oci/debian@sha256%3A124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126?repository_url=index.docker.io/library"
+          }
+        ],
+        "status": "fixed"
+      }
+    ]
+  }
\ No newline at end of file
diff --git a/grype/vulnerability_matcher_test.go b/grype/vulnerability_matcher_test.go
index e72df760..b201ade4 100644
--- a/grype/vulnerability_matcher_test.go
+++ b/grype/vulnerability_matcher_test.go
@@ -21,11 +21,13 @@ import (
 	"github.com/anchore/grype/grype/search"
 	"github.com/anchore/grype/grype/store"
 	"github.com/anchore/grype/grype/version"
+	"github.com/anchore/grype/grype/vex"
 	"github.com/anchore/grype/grype/vulnerability"
 	"github.com/anchore/syft/syft/cpe"
 	"github.com/anchore/syft/syft/file"
 	"github.com/anchore/syft/syft/linux"
 	syftPkg "github.com/anchore/syft/syft/pkg"
+	"github.com/anchore/syft/syft/source"
 )
 
 type ack interface {
@@ -327,6 +329,7 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
 		IgnoreRules    []match.IgnoreRule
 		FailSeverity   *vulnerability.Severity
 		NormalizeByCVE bool
+		VexProcessor   *vex.Processor
 	}
 	type args struct {
 		pkgs    []pkg.Package
@@ -466,6 +469,86 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
 			wantIgnoredMatches: nil,
 			wantErr:            grypeerr.ErrAboveSeverityThreshold,
 		},
+		{
+			name: "pass on severity threshold with VEX",
+			fields: fields{
+				Store:    str,
+				Matchers: matcher.NewDefaultMatchers(matcher.Config{}),
+				FailSeverity: func() *vulnerability.Severity {
+					x := vulnerability.LowSeverity
+					return &x
+				}(),
+				VexProcessor: vex.NewProcessor(vex.ProcessorOptions{
+					Documents: []string{
+						"vex/testdata/vex-docs/openvex-debian.json",
+					},
+					IgnoreRules: []match.IgnoreRule{
+						{
+							VexStatus: "fixed",
+						},
+					},
+				}),
+			},
+			args: args{
+				pkgs: []pkg.Package{
+					neutron2013Pkg,
+				},
+				context: pkg.Context{
+					Source: &source.Description{
+						Name:    "debian",
+						Version: "2013.1.1-1",
+						Metadata: source.StereoscopeImageSourceMetadata{
+							RepoDigests: []string{
+								"debian@sha256:124c7d2707904eea7431fffe91522a01e5a861a624ee31d03372cc1d138a3126",
+							},
+						},
+					},
+					Distro: &linux.Release{
+						ID:        "debian",
+						VersionID: "8",
+					},
+				},
+			},
+			wantMatches: match.NewMatches(),
+			wantIgnoredMatches: []match.IgnoredMatch{
+				{
+					AppliedIgnoreRules: []match.IgnoreRule{
+						{
+							Namespace: "vex",
+							VexStatus: "fixed",
+						},
+					},
+					Match: match.Match{
+						Vulnerability: vulnerability.Vulnerability{
+							Constraint:        version.MustGetConstraint("< 2014.1.3-6", version.DebFormat),
+							ID:                "CVE-2014-fake-1",
+							Namespace:         "debian:distro:debian:8",
+							PackageQualifiers: []qualifier.Qualifier{},
+							CPEs:              []cpe.CPE{},
+							Advisories:        []vulnerability.Advisory{},
+						},
+						Package: neutron2013Pkg,
+						Details: match.Details{
+							{
+								Type: match.ExactDirectMatch,
+								SearchedBy: map[string]any{
+									"distro":    map[string]string{"type": "debian", "version": "8"},
+									"namespace": "debian:distro:debian:8",
+									"package":   map[string]string{"name": "neutron", "version": "2013.1.1-1"},
+								},
+								Found: map[string]any{
+									"versionConstraint": "< 2014.1.3-6 (deb)",
+									"vulnerabilityID":   "CVE-2014-fake-1",
+								},
+								Matcher:    "dpkg-matcher",
+								Confidence: 1,
+							},
+						},
+					},
+				},
+			},
+			wantErr: nil,
+		},
 		{
 			name: "matches by exact-direct match (language)",
 			fields: fields{
@@ -884,6 +967,7 @@ func TestVulnerabilityMatcher_FindMatches(t *testing.T) {
 				IgnoreRules:    tt.fields.IgnoreRules,
 				FailSeverity:   tt.fields.FailSeverity,
 				NormalizeByCVE: tt.fields.NormalizeByCVE,
+				VexProcessor:   tt.fields.VexProcessor,
 			}
 			actualMatches, actualIgnoreMatches, err := m.FindMatches(tt.args.pkgs, tt.args.context)
 			if tt.wantErr != nil {
diff --git a/test/integration/match_by_image_test.go b/test/integration/match_by_image_test.go
index fc66b260..1b04b05c 100644
--- a/test/integration/match_by_image_test.go
+++ b/test/integration/match_by_image_test.go
@@ -1,6 +1,7 @@
 package integration
 
 import (
+	"context"
 	"sort"
 	"strings"
 	"testing"
@@ -21,9 +22,9 @@ import (
 	"github.com/anchore/grype/internal/stringutil"
 	"github.com/anchore/stereoscope/pkg/imagetest"
 	"github.com/anchore/syft/syft"
+	"github.com/anchore/syft/syft/cataloging/pkgcataloging"
 	"github.com/anchore/syft/syft/linux"
 	syftPkg "github.com/anchore/syft/syft/pkg"
-	"github.com/anchore/syft/syft/pkg/cataloger"
 	"github.com/anchore/syft/syft/source"
 )
 
@@ -672,14 +673,13 @@ func TestMatchByImage(t *testing.T) {
 			})
 
 			// TODO: relationships are not verified at this time
-			config := cataloger.DefaultConfig()
-			config.Search.Scope = source.SquashedScope
-
 			// enable all catalogers to cover non default cases
-			config.Catalogers = []string{"all"}
+			config := syft.DefaultCreateSBOMConfig().WithCatalogerSelection(pkgcataloging.NewSelectionRequest().WithDefaults("all"))
+			config.Search.Scope = source.SquashedScope
 
-			collection, _, theDistro, err := syft.CatalogPackages(theSource, config)
+			s, err := syft.CreateSBOM(context.Background(), theSource, config)
 			require.NoError(t, err)
+			require.NotNil(t, s)
 
 			matchers := matcher.NewDefaultMatchers(matcher.Config{})
 
@@ -693,7 +693,7 @@ func TestMatchByImage(t *testing.T) {
 				ExclusionProvider: ep,
 			}
 
-			actualResults := grype.FindVulnerabilitiesForPackage(str, theDistro, matchers, pkg.FromCollection(collection, pkg.SynthesisConfig{}))
+			actualResults := grype.FindVulnerabilitiesForPackage(str, s.Artifacts.LinuxDistribution, matchers, pkg.FromCollection(s.Artifacts.Packages, pkg.SynthesisConfig{}))
 			for _, m := range actualResults.Sorted() {
 				for _, d := range m.Details {
 					observedMatchers.Add(string(d.Matcher))
@@ -701,7 +701,7 @@ func TestMatchByImage(t *testing.T) {
 			}
 
 			// build expected matches from what's discovered from the catalog
-			expectedMatches := test.expectedFn(theSource, collection, theStore)
+			expectedMatches := test.expectedFn(theSource, s.Artifacts.Packages, theStore)
 
 			assertMatches(t, expectedMatches.Sorted(), actualResults.Sorted())
 		})
diff --git a/test/integration/utils_test.go b/test/integration/utils_test.go
index 0ec44733..64f6173f 100644
--- a/test/integration/utils_test.go
+++ b/test/integration/utils_test.go
@@ -2,6 +2,7 @@ package integration
 
 import (
 	"bytes"
+	"context"
 	"errors"
 	"fmt"
 	"os"
@@ -16,7 +17,6 @@ import (
 
 	"github.com/anchore/grype/grype/match"
 	"github.com/anchore/syft/syft"
-	"github.com/anchore/syft/syft/pkg/cataloger"
 	"github.com/anchore/syft/syft/sbom"
 	"github.com/anchore/syft/syft/source"
 )
@@ -73,35 +73,26 @@ func saveImage(t testing.TB, imageName string, destPath string) {
 
 func getSyftSBOM(t testing.TB, image string, encoder sbom.FormatEncoder) string {
 	detection, err := source.Detect(image, source.DetectConfig{})
-	if err != nil {
-		t.Fatalf("could not generate source input for packages command: %+v", err)
-	}
+	require.NoError(t, err)
 
 	src, err := detection.NewSource(source.DetectionSourceConfig{})
-	if err != nil {
-		t.Fatalf("can't get the source: %+v", err)
-	}
+	require.NoError(t, err)
+
 	t.Cleanup(func() {
 		require.NoError(t, src.Close())
 	})
 
-	config := cataloger.DefaultConfig()
+	config := syft.DefaultCreateSBOMConfig()
+
 	config.Search.Scope = source.SquashedScope
 	// TODO: relationships are not verified at this time
-	collection, relationships, distro, err := syft.CatalogPackages(src, config)
-
-	s := sbom.SBOM{
-		Artifacts: sbom.Artifacts{
-			Packages:          collection,
-			LinuxDistribution: distro,
-		},
-		Relationships: relationships,
-		Source:        src.Describe(),
-	}
+	s, err := syft.CreateSBOM(context.Background(), src, config)
+	require.NoError(t, err)
+	require.NotNil(t, s)
 
 	var buf bytes.Buffer
 
-	err = encoder.Encode(&buf, s)
+	err = encoder.Encode(&buf, *s)
 	require.NoError(t, err)
 
 	return buf.String()