Add compliance policy for empty name and version (#3257)

* add policy for empty name and version

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* default stub version

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

* modifying ids requires augmenting relationships

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>

---------

Signed-off-by: Alex Goodman <wagoodman@users.noreply.github.com>
This commit is contained in:
Alex Goodman
2024-09-20 12:50:47 -04:00
committed by GitHub
parent 60bbd24031
commit 963ea594c8
9 changed files with 547 additions and 47 deletions
+46 -10
View File
@@ -17,20 +17,16 @@ type Index struct {
// NewIndex returns a new relationship Index
func NewIndex(relationships ...artifact.Relationship) *Index {
out := Index{}
out := Index{
fromID: make(map[artifact.ID]*mappedRelationships),
toID: make(map[artifact.ID]*mappedRelationships),
}
out.Add(relationships...)
return &out
}
// Add adds all the given relationships to the index, without adding duplicates
func (i *Index) Add(relationships ...artifact.Relationship) {
if i.fromID == nil {
i.fromID = map[artifact.ID]*mappedRelationships{}
}
if i.toID == nil {
i.toID = map[artifact.ID]*mappedRelationships{}
}
// store appropriate indexes for stable ordering to minimize ID() calls
for _, r := range relationships {
// prevent duplicates
@@ -68,6 +64,39 @@ func (i *Index) Add(relationships ...artifact.Relationship) {
}
}
func (i *Index) Remove(id artifact.ID) {
delete(i.fromID, id)
delete(i.toID, id)
for idx := 0; idx < len(i.all); {
if i.all[idx].from == id || i.all[idx].to == id {
i.all = append(i.all[:idx], i.all[idx+1:]...)
} else {
idx++
}
}
}
func (i *Index) Replace(ogID artifact.ID, replacement artifact.Identifiable) {
for _, mapped := range fromMappedByID(i.fromID, ogID) {
i.Add(artifact.Relationship{
From: replacement,
To: mapped.relationship.To,
Type: mapped.relationship.Type,
})
}
for _, mapped := range fromMappedByID(i.toID, ogID) {
i.Add(artifact.Relationship{
From: mapped.relationship.From,
To: replacement,
Type: mapped.relationship.Type,
})
}
i.Remove(ogID)
}
// From returns all relationships from the given identifiable, with specified types
func (i *Index) From(identifiable artifact.Identifiable, types ...artifact.RelationshipType) []artifact.Relationship {
return toSortedSlice(fromMapped(i.fromID, identifiable), types)
@@ -110,10 +139,17 @@ func (i *Index) All(types ...artifact.RelationshipType) []artifact.Relationship
}
func fromMapped(idMap map[artifact.ID]*mappedRelationships, identifiable artifact.Identifiable) []*sortableRelationship {
if identifiable == nil || idMap == nil {
if identifiable == nil {
return nil
}
mapped := idMap[identifiable.ID()]
return fromMappedByID(idMap, identifiable.ID())
}
func fromMappedByID(idMap map[artifact.ID]*mappedRelationships, id artifact.ID) []*sortableRelationship {
if idMap == nil {
return nil
}
mapped := idMap[id]
if mapped == nil {
return nil
}
+177
View File
@@ -3,6 +3,7 @@ package relationship
import (
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"github.com/anchore/syft/syft/artifact"
@@ -231,3 +232,179 @@ func (i id) ID() artifact.ID {
func slice[T any](values ...T) []T {
return values
}
func TestRemove(t *testing.T) {
p1 := pkg.Package{Name: "pkg-1"}
p2 := pkg.Package{Name: "pkg-2"}
p3 := pkg.Package{Name: "pkg-3"}
c1 := file.Coordinates{RealPath: "/coords/1"}
c2 := file.Coordinates{RealPath: "/coords/2"}
c3 := file.Coordinates{RealPath: "/coords/3"}
c4 := file.Coordinates{RealPath: "/coords/4"}
for _, p := range []*pkg.Package{&p1, &p2, &p3} {
p.SetID()
}
r1 := artifact.Relationship{
From: p1,
To: p2,
Type: artifact.DependencyOfRelationship,
}
r2 := artifact.Relationship{
From: p1,
To: p3,
Type: artifact.DependencyOfRelationship,
}
r3 := artifact.Relationship{
From: p1,
To: c1,
Type: artifact.ContainsRelationship,
}
r4 := artifact.Relationship{
From: p2,
To: c2,
Type: artifact.ContainsRelationship,
}
r5 := artifact.Relationship{
From: p3,
To: c1,
Type: artifact.ContainsRelationship,
}
r6 := artifact.Relationship{
From: p3,
To: c2,
Type: artifact.ContainsRelationship,
}
r7 := artifact.Relationship{
From: c1,
To: c3,
Type: artifact.ContainsRelationship,
}
r8 := artifact.Relationship{
From: c3,
To: c4,
Type: artifact.ContainsRelationship,
}
index := NewIndex(r1, r2, r3, r4, r5, r6, r7, r8)
assert.Equal(t, 8, len(index.All()))
// removal of p1 should remove r1, r2, and r3
index.Remove(p1.ID())
remaining := index.All()
assert.Equal(t, 5, len(remaining))
assert.NotContains(t, remaining, r1)
assert.NotContains(t, remaining, r2)
assert.NotContains(t, remaining, r3)
assert.Empty(t, index.From(p1))
assert.Empty(t, index.To(p1))
// removal of c1 should remove r5 and r7
index.Remove(c1.ID())
remaining = index.All()
// r8 remains since c3->c4 should still exist
assert.Equal(t, 3, len(remaining))
assert.NotContains(t, remaining, r5)
assert.NotContains(t, remaining, r7)
assert.Contains(t, remaining, r8)
assert.Empty(t, index.From(c1))
assert.Empty(t, index.To(c1))
// removal of c3 should remove r8
index.Remove(c3.ID())
remaining = index.All()
assert.Equal(t, 2, len(remaining))
assert.Contains(t, remaining, r4)
assert.Contains(t, remaining, r6)
assert.Empty(t, index.From(c3))
assert.Empty(t, index.To(c3))
}
func TestReplace(t *testing.T) {
p1 := pkg.Package{Name: "pkg-1"}
p2 := pkg.Package{Name: "pkg-2"}
p3 := pkg.Package{Name: "pkg-3"}
p4 := pkg.Package{Name: "pkg-4"}
for _, p := range []*pkg.Package{&p1, &p2, &p3, &p4} {
p.SetID()
}
r1 := artifact.Relationship{
From: p1,
To: p2,
Type: artifact.DependencyOfRelationship,
}
r2 := artifact.Relationship{
From: p3,
To: p1,
Type: artifact.DependencyOfRelationship,
}
r3 := artifact.Relationship{
From: p2,
To: p3,
Type: artifact.ContainsRelationship,
}
index := NewIndex(r1, r2, r3)
// replace p1 with p4 in the relationships
index.Replace(p1.ID(), &p4)
expectedRels := []artifact.Relationship{
{
From: p4, // replaced
To: p2,
Type: artifact.DependencyOfRelationship,
},
{
From: p3,
To: p4, // replaced
Type: artifact.DependencyOfRelationship,
},
{
From: p2,
To: p3,
Type: artifact.ContainsRelationship,
},
}
compareRelationships(t, expectedRels, index.All())
}
func compareRelationships(t testing.TB, expected, actual []artifact.Relationship) {
assert.Equal(t, len(expected), len(actual), "number of relationships should match")
for _, e := range expected {
found := false
for _, a := range actual {
if a.From.ID() == e.From.ID() && a.To.ID() == e.To.ID() && a.Type == e.Type {
found = true
break
}
}
assert.True(t, found, "expected relationship not found: %+v", e)
}
}
func TestReplace_NoExistingRelations(t *testing.T) {
p1 := pkg.Package{Name: "pkg-1"}
p2 := pkg.Package{Name: "pkg-2"}
p1.SetID()
p2.SetID()
index := NewIndex()
index.Replace(p1.ID(), &p2)
allRels := index.All()
assert.Len(t, allRels, 0)
}