grype/test/integration/compare_sbom_input_vs_lib_test.go

273 lines
7.8 KiB
Go
Raw Normal View History

package integration
import (
"fmt"
"os"
"testing"
2022-03-03 19:50:24 +00:00
"github.com/scylladb/go-set/strset"
"github.com/stretchr/testify/assert"
"github.com/anchore/grype/grype"
"github.com/anchore/grype/grype/db"
"github.com/anchore/grype/internal"
"github.com/anchore/syft/syft/format/spdxjson"
"github.com/anchore/syft/syft/format/spdxtagvalue"
"github.com/anchore/syft/syft/format/syftjson"
syftPkg "github.com/anchore/syft/syft/pkg"
2022-03-14 21:15:09 +00:00
"github.com/anchore/syft/syft/sbom"
"github.com/anchore/syft/syft/source"
)
Finalize v4 Grype schema (#803) * initial v4 schema setup Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * update v3 => v4 for unit tests -- did NOT update - grype/db/v3/* Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com> * use nullable string in sqlite so null values get represented correctly Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * add missing unit test case for dotnet Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * Add db writer function for calling sqlite vacuum Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * adding normalization of package names at database adapter layer Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * refactor namespaces for v4 Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * update v4 stuff to use sqlite fork Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * Namespace should satisfy Stringer interface Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * normalize CPEs before comparison Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * vulnerability exclusion => vulnerability match exclusion Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * updates to vulnerability match exclusion models Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * add initial vulnerability match exclusion store unit tests Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * make vuln match exclusion constraints nullable Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * move vuln match namespace into constraints object and refactor Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * check db match constraints to ensure there aren't any unknown fields and add json hints Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * ensure we only keep compatible match exclusion constraints Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * use omitempty on all match exclusion structs Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * remove db v4 schema resolver and namespace types Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * rename Vacuum to Close Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * lint fixes + remove panic on vuln provider creation Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * WIP match exclusions Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * build list of ignore rules from v4 db records Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * quick attempt at a new uber object Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * just pass around the full object for now to quickly get to a usable state Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * fix panic when no vuln db loaded Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * use interfaces for db.store function signatures Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * Flatten the match exclusion constraint model to simplify logic Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * updating some tests Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * fix panic when no db update possible Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * more tests Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * WIP fixing match exclusion constraint usability and json mapping logic Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * add v4 db diff logic (excluding vulnerability_match_exclusion data for now) Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * lint fix Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * update integration tests Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * nvd -> nvd:cpe namespace updates Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * ensure test store uses v4 normalized names Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * set the grype db update url to staging for v4 Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * prevent more segfaults on database open Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * add continue when unable to load ignore rules Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * remove db.Status from the Store object Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * fix compare_sbom_input_vs_lib_test.go Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * remove staging endpoint now that v4 is published Signed-off-by: Weston Steimel <weston.steimel@anchore.com> Co-authored-by: Christopher Phillips <christopher.phillips@anchore.com> Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
2022-07-05 18:03:16 +00:00
func getListingURL() string {
if value, ok := os.LookupEnv("GRYPE_DB_UPDATE_URL"); ok {
return value
}
return internal.DBUpdateURL
}
func must(e sbom.FormatEncoder, err error) sbom.FormatEncoder {
if err != nil {
panic(err)
}
return e
}
func TestCompareSBOMInputToLibResults(t *testing.T) {
// get a grype DB
store, _, closer, err := grype.LoadVulnerabilityDB(db.Config{
DBRootDir: "test-fixtures/grype-db",
Finalize v4 Grype schema (#803) * initial v4 schema setup Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * update v3 => v4 for unit tests -- did NOT update - grype/db/v3/* Signed-off-by: Christopher Phillips <christopher.phillips@anchore.com> * use nullable string in sqlite so null values get represented correctly Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * add missing unit test case for dotnet Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * Add db writer function for calling sqlite vacuum Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * adding normalization of package names at database adapter layer Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * refactor namespaces for v4 Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * update v4 stuff to use sqlite fork Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * Namespace should satisfy Stringer interface Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * normalize CPEs before comparison Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * vulnerability exclusion => vulnerability match exclusion Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * updates to vulnerability match exclusion models Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * add initial vulnerability match exclusion store unit tests Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * make vuln match exclusion constraints nullable Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * move vuln match namespace into constraints object and refactor Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * check db match constraints to ensure there aren't any unknown fields and add json hints Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * ensure we only keep compatible match exclusion constraints Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * use omitempty on all match exclusion structs Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * remove db v4 schema resolver and namespace types Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * rename Vacuum to Close Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * lint fixes + remove panic on vuln provider creation Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * WIP match exclusions Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * build list of ignore rules from v4 db records Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * quick attempt at a new uber object Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * just pass around the full object for now to quickly get to a usable state Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * fix panic when no vuln db loaded Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * use interfaces for db.store function signatures Signed-off-by: Alex Goodman <alex.goodman@anchore.com> * Flatten the match exclusion constraint model to simplify logic Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * updating some tests Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * fix panic when no db update possible Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * more tests Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * WIP fixing match exclusion constraint usability and json mapping logic Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * add v4 db diff logic (excluding vulnerability_match_exclusion data for now) Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * lint fix Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * update integration tests Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * nvd -> nvd:cpe namespace updates Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * ensure test store uses v4 normalized names Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * set the grype db update url to staging for v4 Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * prevent more segfaults on database open Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * add continue when unable to load ignore rules Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * remove db.Status from the Store object Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * fix compare_sbom_input_vs_lib_test.go Signed-off-by: Weston Steimel <weston.steimel@anchore.com> * remove staging endpoint now that v4 is published Signed-off-by: Weston Steimel <weston.steimel@anchore.com> Co-authored-by: Christopher Phillips <christopher.phillips@anchore.com> Co-authored-by: Alex Goodman <alex.goodman@anchore.com>
2022-07-05 18:03:16 +00:00
ListingURL: getListingURL(),
ValidateByHashOnGet: false,
}, true)
assert.NoError(t, err)
if closer != nil {
defer closer.Close()
}
definedPkgTypes := strset.New()
for _, p := range syftPkg.AllPkgs {
definedPkgTypes.Add(string(p))
}
2023-01-18 17:50:46 +00:00
// exceptions: rust, php, dart, msrc (kb), etc. are not under test
definedPkgTypes.Remove(
string(syftPkg.BinaryPkg), // these are removed due to overlap-by-file-ownership
string(syftPkg.PhpPeclPkg),
string(syftPkg.RustPkg),
string(syftPkg.KbPkg),
string(syftPkg.DartPubPkg),
2022-05-13 16:46:31 +00:00
string(syftPkg.DotnetPkg),
string(syftPkg.PhpComposerPkg),
2022-07-06 14:48:21 +00:00
string(syftPkg.ConanPkg),
2023-01-18 17:50:46 +00:00
string(syftPkg.HexPkg),
2022-07-11 19:15:12 +00:00
string(syftPkg.PortagePkg),
string(syftPkg.CocoapodsPkg),
string(syftPkg.HackagePkg),
string(syftPkg.NixPkg),
string(syftPkg.JenkinsPluginPkg), // package type cannot be inferred for all formats
string(syftPkg.LinuxKernelPkg),
string(syftPkg.LinuxKernelModulePkg),
string(syftPkg.Rpkg),
2024-08-09 18:32:05 +00:00
string(syftPkg.SwiplPackPkg),
string(syftPkg.SwiftPkg),
string(syftPkg.GithubActionPkg),
string(syftPkg.GithubActionWorkflowPkg),
string(syftPkg.ErlangOTPPkg),
string(syftPkg.WordpressPluginPkg), // TODO: remove me when there is a matcher for this merged in https://github.com/anchore/grype/pull/1553
string(syftPkg.LuaRocksPkg),
)
observedPkgTypes := strset.New()
testCases := []struct {
name string
image string
format sbom.FormatEncoder
}{
{
image: "anchore/test_images:vulnerabilities-alpine",
format: syftjson.NewFormatEncoder(),
name: "alpine-syft-json",
},
{
image: "anchore/test_images:vulnerabilities-alpine",
format: must(spdxjson.NewFormatEncoderWithConfig(spdxjson.DefaultEncoderConfig())),
name: "alpine-spdx-json",
},
{
image: "anchore/test_images:vulnerabilities-alpine",
format: must(spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.DefaultEncoderConfig())),
name: "alpine-spdx-tag-value",
},
{
image: "anchore/test_images:gems",
format: syftjson.NewFormatEncoder(),
name: "gems-syft-json",
},
{
image: "anchore/test_images:gems",
format: must(spdxjson.NewFormatEncoderWithConfig(spdxjson.DefaultEncoderConfig())),
name: "gems-spdx-json",
},
{
image: "anchore/test_images:gems",
format: must(spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.DefaultEncoderConfig())),
name: "gems-spdx-tag-value",
},
{
image: "anchore/test_images:vulnerabilities-debian",
format: syftjson.NewFormatEncoder(),
name: "debian-syft-json",
},
{
image: "anchore/test_images:vulnerabilities-debian",
format: must(spdxjson.NewFormatEncoderWithConfig(spdxjson.DefaultEncoderConfig())),
name: "debian-spdx-json",
},
{
image: "anchore/test_images:vulnerabilities-debian",
format: must(spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.DefaultEncoderConfig())),
name: "debian-spdx-tag-value",
},
{
image: "anchore/test_images:vulnerabilities-centos",
format: syftjson.NewFormatEncoder(),
name: "centos-syft-json",
},
{
image: "anchore/test_images:vulnerabilities-centos",
format: must(spdxjson.NewFormatEncoderWithConfig(spdxjson.DefaultEncoderConfig())),
name: "centos-spdx-json",
},
{
image: "anchore/test_images:vulnerabilities-centos",
format: must(spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.DefaultEncoderConfig())),
name: "centos-spdx-tag-value",
},
{
image: "anchore/test_images:npm",
format: syftjson.NewFormatEncoder(),
name: "npm-syft-json",
},
{
image: "anchore/test_images:npm",
format: must(spdxjson.NewFormatEncoderWithConfig(spdxjson.DefaultEncoderConfig())),
name: "npm-spdx-json",
},
{
image: "anchore/test_images:npm",
format: must(spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.DefaultEncoderConfig())),
name: "npm-spdx-tag-value",
},
{
image: "anchore/test_images:java",
format: syftjson.NewFormatEncoder(),
name: "java-syft-json",
},
{
image: "anchore/test_images:java",
format: must(spdxjson.NewFormatEncoderWithConfig(spdxjson.DefaultEncoderConfig())),
name: "java-spdx-json",
},
{
image: "anchore/test_images:java",
format: must(spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.DefaultEncoderConfig())),
name: "java-spdx-tag-value",
},
{
image: "anchore/test_images:golang-56d52bc",
format: syftjson.NewFormatEncoder(),
name: "go-syft-json",
},
{
image: "anchore/test_images:golang-56d52bc",
format: must(spdxjson.NewFormatEncoderWithConfig(spdxjson.DefaultEncoderConfig())),
name: "go-spdx-json",
},
{
image: "anchore/test_images:golang-56d52bc",
format: must(spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.DefaultEncoderConfig())),
name: "go-spdx-tag-value",
},
{
image: "anchore/test_images:arch",
format: syftjson.NewFormatEncoder(),
name: "arch-syft-json",
},
{
image: "anchore/test_images:arch",
format: must(spdxjson.NewFormatEncoderWithConfig(spdxjson.DefaultEncoderConfig())),
name: "arch-spdx-json",
},
{
image: "anchore/test_images:arch",
format: must(spdxtagvalue.NewFormatEncoderWithConfig(spdxtagvalue.DefaultEncoderConfig())),
name: "arch-spdx-tag-value",
},
}
for _, tc := range testCases {
imageArchive := PullThroughImageCache(t, tc.image)
t.Run(tc.name, func(t *testing.T) {
// get SBOM from syft, write to temp file
sbomBytes := getSyftSBOM(t, imageArchive, "docker-archive", tc.format)
sbomFile, err := os.CreateTemp("", "")
assert.NoError(t, err)
t.Cleanup(func() {
assert.NoError(t, os.Remove(sbomFile.Name()))
})
_, err = sbomFile.WriteString(sbomBytes)
assert.NoError(t, err)
assert.NoError(t, sbomFile.Close())
// get vulns (sbom)
matchesFromSbom, _, pkgsFromSbom, err := grype.FindVulnerabilities(*store, fmt.Sprintf("sbom:%s", sbomFile.Name()), source.SquashedScope, nil)
assert.NoError(t, err)
// get vulns (image)
imageSource := fmt.Sprintf("docker-archive:%s", imageArchive)
matchesFromImage, _, _, err := grype.FindVulnerabilities(*store, imageSource, source.SquashedScope, nil)
assert.NoError(t, err)
// compare packages (shallow)
matchSetFromSbom := getMatchSet(matchesFromSbom)
matchSetFromImage := getMatchSet(matchesFromImage)
assert.Empty(t, strset.Difference(matchSetFromSbom, matchSetFromImage).List(), "vulnerabilities present only in results when using sbom as input")
assert.Empty(t, strset.Difference(matchSetFromImage, matchSetFromSbom).List(), "vulnerabilities present only in results when using image as input")
// track all covered package types (for use after the test)
for _, p := range pkgsFromSbom {
observedPkgTypes.Add(string(p.Type))
2022-03-14 21:15:09 +00:00
}
})
}
// ensure we've covered all package types (-rust, -kb)
unobservedPackageTypes := strset.Difference(definedPkgTypes, observedPkgTypes)
assert.Empty(t, unobservedPackageTypes.List(), "not all package type were covered in testing")
}