github.com/anchore/syft@v1.38.2/internal/task/package_task_factory_test.go (about)

     1  package task
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/stretchr/testify/assert"
     7  	"github.com/stretchr/testify/require"
     8  
     9  	"github.com/anchore/syft/syft/artifact"
    10  	"github.com/anchore/syft/syft/cataloging"
    11  	"github.com/anchore/syft/syft/cpe"
    12  	"github.com/anchore/syft/syft/file"
    13  	"github.com/anchore/syft/syft/pkg"
    14  )
    15  
    16  func Test_hasAuthoritativeCPE(t *testing.T) {
    17  	tests := []struct {
    18  		name string
    19  		cpes []cpe.CPE
    20  		want bool
    21  	}{
    22  		{
    23  			name: "no cpes",
    24  			cpes: []cpe.CPE{},
    25  			want: false,
    26  		},
    27  		{
    28  			name: "no authoritative cpes",
    29  			cpes: []cpe.CPE{
    30  				{
    31  					Source: cpe.GeneratedSource,
    32  				},
    33  			},
    34  			want: false,
    35  		},
    36  		{
    37  			name: "has declared (authoritative) cpe",
    38  			cpes: []cpe.CPE{
    39  				{
    40  					Source: cpe.DeclaredSource,
    41  				},
    42  			},
    43  			want: true,
    44  		},
    45  		{
    46  			name: "has lookup (authoritative) cpe",
    47  			cpes: []cpe.CPE{
    48  				{
    49  					Source: cpe.NVDDictionaryLookupSource,
    50  				},
    51  			},
    52  			want: true,
    53  		},
    54  	}
    55  	for _, tt := range tests {
    56  		t.Run(tt.name, func(t *testing.T) {
    57  			assert.Equal(t, tt.want, hasAuthoritativeCPE(tt.cpes))
    58  		})
    59  	}
    60  }
    61  
    62  func TestApplyCompliance(t *testing.T) {
    63  	p1 := pkg.Package{Name: "pkg-1", Version: "1.0"}
    64  	p2 := pkg.Package{Name: "", Version: "1.0"}   // missing name
    65  	p3 := pkg.Package{Name: "pkg-3", Version: ""} // missing version
    66  	p4 := pkg.Package{Name: "pkg-4", Version: ""} // missing version
    67  	c1 := file.Coordinates{RealPath: "/coords/1"}
    68  	c2 := file.Coordinates{RealPath: "/coords/2"}
    69  
    70  	for _, p := range []*pkg.Package{&p1, &p2, &p3, &p4} {
    71  		p.SetID()
    72  	}
    73  
    74  	r1 := artifact.Relationship{
    75  		From: p1,
    76  		To:   c1,
    77  		Type: artifact.ContainsRelationship,
    78  	}
    79  	r2 := artifact.Relationship{
    80  		From: p2,
    81  		To:   c2,
    82  		Type: artifact.ContainsRelationship,
    83  	}
    84  
    85  	cfg := cataloging.ComplianceConfig{
    86  		MissingName:    cataloging.ComplianceActionDrop,
    87  		MissingVersion: cataloging.ComplianceActionStub,
    88  	}
    89  
    90  	remainingPkgs, remainingRels := applyCompliance(cfg, []pkg.Package{p1, p2, p3, p4}, []artifact.Relationship{r1, r2})
    91  
    92  	// p2 should be dropped because it has a missing name, p3 and p4 should pass with a warning for the missing version
    93  	assert.Len(t, remainingPkgs, 3) // p1, p3, p4 should remain
    94  	assert.Len(t, remainingRels, 1) // only r1 should remain (relationship involving p1)
    95  }
    96  
    97  func TestFilterNonCompliantPackages(t *testing.T) {
    98  	p1 := pkg.Package{Name: "pkg-1", Version: "1.0"}
    99  	p2 := pkg.Package{Name: "", Version: "1.0"}   // missing name
   100  	p3 := pkg.Package{Name: "pkg-3", Version: ""} // missing version
   101  
   102  	for _, p := range []*pkg.Package{&p1, &p2, &p3} {
   103  		p.SetID()
   104  	}
   105  
   106  	cfg := cataloging.ComplianceConfig{
   107  		MissingName:    cataloging.ComplianceActionDrop,
   108  		MissingVersion: cataloging.ComplianceActionKeep,
   109  	}
   110  
   111  	remainingPkgs, droppedPkgs, replacement := filterNonCompliantPackages([]pkg.Package{p1, p2, p3}, cfg)
   112  	require.Nil(t, replacement)
   113  
   114  	// p2 should be dropped because it has a missing name
   115  	assert.Len(t, remainingPkgs, 2)
   116  	assert.Len(t, droppedPkgs, 1)
   117  	assert.Equal(t, p2, droppedPkgs[0])
   118  }
   119  
   120  func TestApplyLicenseContentRules(t *testing.T) {
   121  	licenseWithSPDX := pkg.License{
   122  		SPDXExpression: "MIT",
   123  		Contents:       "MIT license content",
   124  	}
   125  	licenseWithoutSPDX := pkg.License{
   126  		Value:    "License-Not-A-SPDX-Expression",
   127  		Contents: "Non-SPDX license content",
   128  	}
   129  
   130  	tests := []struct {
   131  		name             string
   132  		inputLicenses    []pkg.License
   133  		cfg              cataloging.LicenseConfig
   134  		expectedLicenses []pkg.License
   135  	}{
   136  		{
   137  			name: "LicenseContentIncludeUnknown",
   138  			inputLicenses: []pkg.License{
   139  				licenseWithSPDX,
   140  				licenseWithoutSPDX,
   141  			},
   142  			cfg: cataloging.LicenseConfig{
   143  				IncludeContent: cataloging.LicenseContentIncludeUnknown,
   144  			},
   145  			expectedLicenses: []pkg.License{
   146  				{
   147  					SPDXExpression: "MIT",
   148  					Contents:       "", // content cleared for SPDX license
   149  				},
   150  				{
   151  					Value:    "License-Not-A-SPDX-Expression",
   152  					Contents: "Non-SPDX license content", // content preserved for non-SPDX
   153  				},
   154  			},
   155  		},
   156  		{
   157  			name: "LicenseContentExcludeAll",
   158  			inputLicenses: []pkg.License{
   159  				licenseWithSPDX,
   160  				licenseWithoutSPDX,
   161  			},
   162  			cfg: cataloging.LicenseConfig{
   163  				IncludeContent: cataloging.LicenseContentExcludeAll,
   164  			},
   165  			expectedLicenses: []pkg.License{
   166  				{
   167  					SPDXExpression: "MIT",
   168  					Contents:       "", // content cleared
   169  				},
   170  				{
   171  					Value:    "License-Not-A-SPDX-Expression",
   172  					Contents: "", // content cleared
   173  				},
   174  			},
   175  		},
   176  		{
   177  			name: "LicenseContentIncludeAll",
   178  			inputLicenses: []pkg.License{
   179  				licenseWithSPDX,
   180  				licenseWithoutSPDX,
   181  			},
   182  			cfg: cataloging.LicenseConfig{
   183  				IncludeContent: cataloging.LicenseContentIncludeAll,
   184  			},
   185  			expectedLicenses: []pkg.License{
   186  				{
   187  					SPDXExpression: "MIT",
   188  					Contents:       "MIT license content", // content preserved
   189  				},
   190  				{
   191  					Value:    "License-Not-A-SPDX-Expression",
   192  					Contents: "Non-SPDX license content", // content preserved
   193  				},
   194  			},
   195  		},
   196  		{
   197  			name: "default license config should be LicenseContentExcludeAll",
   198  			inputLicenses: []pkg.License{
   199  				licenseWithSPDX,
   200  				licenseWithoutSPDX,
   201  			},
   202  			cfg: cataloging.DefaultLicenseConfig(),
   203  			expectedLicenses: []pkg.License{
   204  				{
   205  					SPDXExpression: "MIT",
   206  				},
   207  				{
   208  					Value: "License-Not-A-SPDX-Expression",
   209  				},
   210  			},
   211  		},
   212  		{
   213  			name: "invalid license content cataloging config results in the default case",
   214  			inputLicenses: []pkg.License{
   215  				licenseWithSPDX,
   216  				licenseWithoutSPDX,
   217  			},
   218  			cfg: cataloging.LicenseConfig{
   219  				IncludeContent: cataloging.LicenseContent("invalid"),
   220  			},
   221  			expectedLicenses: []pkg.License{
   222  				{
   223  					SPDXExpression: "MIT",
   224  				},
   225  				{
   226  					Value:    "License-Not-A-SPDX-Expression",
   227  					Contents: "", // content all removed
   228  				},
   229  			},
   230  		},
   231  		{
   232  			name:          "Empty licenses",
   233  			inputLicenses: []pkg.License{},
   234  			cfg: cataloging.LicenseConfig{
   235  				IncludeContent: cataloging.LicenseContentIncludeAll,
   236  			},
   237  			expectedLicenses: []pkg.License{},
   238  		},
   239  	}
   240  
   241  	for _, tt := range tests {
   242  		t.Run(tt.name, func(t *testing.T) {
   243  			inputPkg := &pkg.Package{
   244  				Licenses: pkg.NewLicenseSet(tt.inputLicenses...),
   245  			}
   246  
   247  			inputPkg.SetID()
   248  			originalID := inputPkg.ID()
   249  
   250  			applyLicenseContentRules(inputPkg, tt.cfg)
   251  
   252  			assert.Equal(t, originalID, inputPkg.ID(), "package ID changed unexpectedly")
   253  
   254  			actualLicenses := inputPkg.Licenses.ToSlice()
   255  			expectedLicenses := pkg.NewLicenseSet(tt.expectedLicenses...).ToSlice()
   256  
   257  			assert.Equal(t, expectedLicenses, actualLicenses, "license contents do not match expected values")
   258  
   259  		})
   260  	}
   261  }
   262  
   263  func TestApplyComplianceRules_DropAndStub(t *testing.T) {
   264  	p := pkg.Package{Name: "", Version: ""}
   265  	p.SetID()
   266  	ogID := p.ID()
   267  
   268  	cfg := cataloging.ComplianceConfig{
   269  		MissingName:    cataloging.ComplianceActionDrop,
   270  		MissingVersion: cataloging.ComplianceActionStub,
   271  	}
   272  
   273  	isCompliant, replacement := applyComplianceRules(&p, cfg)
   274  	require.NotNil(t, replacement)
   275  	assert.Equal(t, packageReplacement{
   276  		original: ogID,
   277  		pkg:      p,
   278  	}, *replacement)
   279  
   280  	// the package should be dropped due to missing name (drop action) and its version should be stubbed
   281  	assert.False(t, isCompliant)
   282  	assert.Equal(t, cataloging.UnknownStubValue, p.Version)
   283  }