github.com/anchore/syft@v1.38.2/internal/relationship/exclude_binaries_by_file_ownership_overlap_test.go (about)

     1  package relationship
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  
     8  	"github.com/anchore/syft/syft/artifact"
     9  	"github.com/anchore/syft/syft/pkg"
    10  )
    11  
    12  func TestExcludeByFileOwnershipOverlap(t *testing.T) {
    13  	packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg}
    14  	packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg, Metadata: pkg.JavaVMInstallation{}}
    15  	packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{Type: "rpm"}}
    16  	packageD := pkg.Package{Name: "package-d", Type: pkg.BitnamiPkg}
    17  	for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD} {
    18  		p := p
    19  		p.SetID()
    20  	}
    21  
    22  	tests := []struct {
    23  		name          string
    24  		relationship  artifact.Relationship
    25  		packages      *pkg.Collection
    26  		shouldExclude bool
    27  	}{
    28  		{
    29  			// prove that OS -> bin exclusions are wired
    30  			name: "exclusions from os -> elf binary (as RPM)",
    31  			relationship: artifact.Relationship{
    32  				Type: artifact.OwnershipByFileOverlapRelationship,
    33  				From: packageA, // OS
    34  				To:   packageC, // ELF binary
    35  			},
    36  			packages:      pkg.NewCollection(packageA, packageC),
    37  			shouldExclude: true,
    38  		},
    39  		{
    40  			// prove that bin -> JVM exclusions are wired
    41  			name: "exclusions from binary -> binary with JVM metadata",
    42  			relationship: artifact.Relationship{
    43  				Type: artifact.OwnershipByFileOverlapRelationship,
    44  				From: packageB, // binary with JVM metadata
    45  				To:   packageC, // binary
    46  			},
    47  			packages:      pkg.NewCollection(packageC, packageB),
    48  			shouldExclude: true,
    49  		},
    50  		{
    51  			// prove that Bitnami -> bin exclusions are wired
    52  			name: "exclusions from bitnami -> binary",
    53  			relationship: artifact.Relationship{
    54  				Type: artifact.OwnershipByFileOverlapRelationship,
    55  				From: packageD, // Bitnami
    56  				To:   packageC, // ELF binary
    57  			},
    58  			packages:      pkg.NewCollection(packageD, packageC),
    59  			shouldExclude: true,
    60  		},
    61  	}
    62  
    63  	for _, test := range tests {
    64  		t.Run(test.name, func(t *testing.T) {
    65  			actualExclude := excludeByFileOwnershipOverlap(test.relationship, test.packages)
    66  			didExclude := actualExclude != ""
    67  			if !didExclude && test.shouldExclude {
    68  				t.Errorf("expected to exclude relationship %+v", test.relationship)
    69  			}
    70  		})
    71  
    72  	}
    73  }
    74  
    75  func TestIdentifyOverlappingOSRelationship(t *testing.T) {
    76  	packageA := pkg.Package{Name: "package-a", Type: pkg.ApkPkg} // OS package
    77  	packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg}
    78  	packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.BinarySignature{}}
    79  	packageD := pkg.Package{Name: "package-d", Type: pkg.PythonPkg} // Language package
    80  	packageE := pkg.Package{Name: "package-e", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{}}
    81  
    82  	for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE} {
    83  		p.SetID()
    84  	}
    85  
    86  	tests := []struct {
    87  		name       string
    88  		parent     *pkg.Package
    89  		child      *pkg.Package
    90  		expectedID artifact.ID
    91  	}{
    92  		{
    93  			name:       "OS -> binary without metadata",
    94  			parent:     &packageA,
    95  			child:      &packageB,
    96  			expectedID: packageB.ID(), // OS package to binary package, should return child ID
    97  		},
    98  		{
    99  			name:       "OS -> binary with binary metadata",
   100  			parent:     &packageA,
   101  			child:      &packageC,
   102  			expectedID: packageC.ID(), // OS package to binary package with binary metadata, should return child ID
   103  		},
   104  		{
   105  			name:       "OS -> non-binary package",
   106  			parent:     &packageA,
   107  			child:      &packageD,
   108  			expectedID: "", // OS package to non-binary package, no exclusion
   109  		},
   110  		{
   111  			name:       "OS -> binary with ELF metadata",
   112  			parent:     &packageA,
   113  			child:      &packageE,
   114  			expectedID: packageE.ID(), // OS package to binary package with ELF metadata, should return child ID
   115  		},
   116  		{
   117  			name:       "non-OS parent",
   118  			parent:     &packageD, // non-OS package
   119  			child:      &packageC,
   120  			expectedID: "", // non-OS parent, no exclusion
   121  		},
   122  	}
   123  
   124  	for _, tt := range tests {
   125  		t.Run(tt.name, func(t *testing.T) {
   126  			resultID := identifyOverlappingOSRelationship(tt.parent, tt.child)
   127  			assert.Equal(t, tt.expectedID, resultID)
   128  		})
   129  	}
   130  }
   131  
   132  func TestIdentifyOverlappingBitnamiRelationship(t *testing.T) {
   133  	packageA := pkg.Package{Name: "package-a", Type: pkg.BitnamiPkg} // Bitnami package
   134  	packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg}
   135  	packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.BinarySignature{}}
   136  	packageD := pkg.Package{Name: "package-d", Type: pkg.PythonPkg} // Language package
   137  	packageE := pkg.Package{Name: "package-e", Type: pkg.BinaryPkg, Metadata: pkg.ELFBinaryPackageNoteJSONPayload{}}
   138  
   139  	for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE} {
   140  		p.SetID()
   141  	}
   142  
   143  	tests := []struct {
   144  		name       string
   145  		parent     *pkg.Package
   146  		child      *pkg.Package
   147  		expectedID artifact.ID
   148  	}{
   149  		{
   150  			name:       "Bitnami -> binary without metadata",
   151  			parent:     &packageA,
   152  			child:      &packageB,
   153  			expectedID: packageB.ID(), // Bitnami package to binary package, should return child ID
   154  		},
   155  		{
   156  			name:       "Bitnami -> binary with binary metadata",
   157  			parent:     &packageA,
   158  			child:      &packageC,
   159  			expectedID: packageC.ID(), // Bitnami package to binary package with binary metadata, should return child ID
   160  		},
   161  		{
   162  			name:       "Bitnami -> non-binary package",
   163  			parent:     &packageA,
   164  			child:      &packageD,
   165  			expectedID: "", // Bitnami package to non-binary package, no exclusion
   166  		},
   167  		{
   168  			name:       "Bitnami -> binary with ELF metadata",
   169  			parent:     &packageA,
   170  			child:      &packageE,
   171  			expectedID: packageE.ID(), // Bitnami package to binary package with ELF metadata, should return child ID
   172  		},
   173  		{
   174  			name:       "non-Bitnami parent",
   175  			parent:     &packageD, // non-Bitnami package
   176  			child:      &packageC,
   177  			expectedID: "", // non-Bitnami parent, no exclusion
   178  		},
   179  	}
   180  
   181  	for _, tt := range tests {
   182  		t.Run(tt.name, func(t *testing.T) {
   183  			resultID := identifyOverlappingBitnamiRelationship(tt.parent, tt.child)
   184  			assert.Equal(t, tt.expectedID, resultID)
   185  		})
   186  	}
   187  }
   188  
   189  func TestIdentifyOverlappingJVMRelationship(t *testing.T) {
   190  
   191  	packageA := pkg.Package{Name: "package-a", Type: pkg.BinaryPkg}
   192  	packageB := pkg.Package{Name: "package-b", Type: pkg.BinaryPkg, Metadata: pkg.BinarySignature{}}
   193  	packageC := pkg.Package{Name: "package-c", Type: pkg.BinaryPkg, Metadata: pkg.JavaVMInstallation{}}
   194  	packageD := pkg.Package{Name: "package-d", Type: pkg.PythonPkg}
   195  	packageE := pkg.Package{Name: "package-e", Type: pkg.BinaryPkg}
   196  
   197  	for _, p := range []*pkg.Package{&packageA, &packageB, &packageC, &packageD, &packageE} {
   198  		p.SetID()
   199  	}
   200  
   201  	tests := []struct {
   202  		name       string
   203  		parent     *pkg.Package
   204  		child      *pkg.Package
   205  		expectedID artifact.ID
   206  	}{
   207  		{
   208  			name:       "binary -> binary with JVM installation",
   209  			parent:     &packageA,
   210  			child:      &packageC,
   211  			expectedID: packageA.ID(), // JVM found, return BinaryPkg ID
   212  		},
   213  		{
   214  			name:       "binary -> binary with binary signature",
   215  			parent:     &packageA,
   216  			child:      &packageB,
   217  			expectedID: "", // binary signatures only found, no exclusion
   218  		},
   219  		{
   220  			name:       "binary -> python (non-binary child)",
   221  			parent:     &packageA,
   222  			child:      &packageD,
   223  			expectedID: "", // non-binary child, no exclusion
   224  		},
   225  		{
   226  			name:       "no JVM or signature in binary -> binary",
   227  			parent:     &packageA,
   228  			child:      &packageE,
   229  			expectedID: "", // no JVM or binary signature, no exclusion
   230  		},
   231  		{
   232  			name:       "non-binary parent",
   233  			parent:     &packageD,
   234  			child:      &packageC,
   235  			expectedID: "", // non-binary parent, no exclusion
   236  		},
   237  	}
   238  
   239  	for _, tt := range tests {
   240  		t.Run(tt.name, func(t *testing.T) {
   241  			resultID := identifyOverlappingJVMRelationship(tt.parent, tt.child)
   242  			assert.Equal(t, tt.expectedID, resultID)
   243  		})
   244  	}
   245  }