diff --git a/grype/vex/openvex/implementation.go b/grype/vex/openvex/implementation.go index 608750bae7e..e63cc46e11d 100644 --- a/grype/vex/openvex/implementation.go +++ b/grype/vex/openvex/implementation.go @@ -4,6 +4,7 @@ import ( "errors" "fmt" "net/url" + "slices" "strings" "github.com/google/go-containerregistry/pkg/name" @@ -61,16 +62,35 @@ func (ovm *Processor) ReadVexDocuments(docs []string) (interface{}, error) { func productIdentifiersFromContext(pkgContext *pkg.Context) ([]string, error) { switch v := pkgContext.Source.Metadata.(type) { case source.ImageMetadata: - // TODO(puerco): We can create a wider definition here. This effectively - // adds the multiarch image and the image of the OS running grype. We - // could generate more identifiers to match better. - return identifiersFromDigests(v.RepoDigests), nil + tagIdentifiers := identifiersFromTags(v.Tags, pkgContext.Source.Name) + digestIdentifiers := identifiersFromDigests(v.RepoDigests) + identifiers := slices.Concat(tagIdentifiers, digestIdentifiers) + return identifiers, nil default: // Fail for now return nil, errors.New("source type not supported for VEX") } } +func identifiersFromTags(tags []string, name string) []string { + identifiers := []string{} + + for _, tag := range tags { + identifiers = append(identifiers, tag) + + tagMap := map[string]string{} + _, splitTag, found := strings.Cut(tag, ":") + if found { + tagMap["tag"] = splitTag + qualifiers := packageurl.QualifiersFromMap(tagMap) + + identifiers = append(identifiers, packageurl.NewPackageURL("oci", "", name, "", qualifiers, "").String()) + } + } + + return identifiers +} + func identifiersFromDigests(digests []string) []string { identifiers := []string{} diff --git a/grype/vex/openvex/implementation_test.go b/grype/vex/openvex/implementation_test.go index 6407df46e24..93dd03f319f 100644 --- a/grype/vex/openvex/implementation_test.go +++ b/grype/vex/openvex/implementation_test.go @@ -6,6 +6,28 @@ import ( "github.com/stretchr/testify/require" ) +func TestIdentifiersFromTags(t *testing.T) { + for _, tc := range []struct { + sut string + name string + expected []string + }{ + { + "alpine:v1.2.3", + "alpine", + []string{"alpine:v1.2.3", "pkg:oci/alpine?tag=v1.2.3"}, + }, + { + "alpine", + "alpine", + []string{"alpine"}, + }, + } { + res := identifiersFromTags([]string{tc.sut}, tc.name) + require.Equal(t, tc.expected, res) + } +} + func TestIdentifiersFromDigests(t *testing.T) { for _, tc := range []struct { sut string