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 }