8787 },
8888 Licenses : []string {"GPL-2.0" },
8989 }
90+
91+ orphanedApkComponent = & core.Component {
92+ Type : core .TypeLibrary ,
93+ Name : "orphaned-package" ,
94+ Version : "1.0.0" ,
95+ PkgIdentifier : ftypes.PkgIdentifier {
96+ PURL : & packageurl.PackageURL {
97+ Type : packageurl .TypeApk ,
98+ Name : "orphaned-package" ,
99+ Version : "1.0.0" ,
100+ Qualifiers : packageurl.Qualifiers {
101+ {Key : "arch" , Value : "aarch64" },
102+ {Key : "distro" , Value : "wolfi-20230201" },
103+ },
104+ },
105+ },
106+ Licenses : []string {"MIT" },
107+ }
90108)
91109
92110func TestDecoder_Decode_OSPackages (t * testing.T ) {
@@ -141,16 +159,17 @@ func TestDecoder_Decode_OSPackages(t *testing.T) {
141159 },
142160 },
143161 {
144- name : "multiple OS packages without OS metadata should be included" ,
162+ name : "multiple OS packages without OS metadata should be included and merged " ,
145163 setupBOM : func () * core.BOM {
146164 bom := core .NewBOM (core.Options {})
147165 bom .SerialNumber = "test-multiple-no-os"
148166 bom .Version = 1
149167
150- // Add multiple APK packages
168+ // Add multiple APK packages including orphaned package
151169 bom .AddComponent (apkToolsComponent )
152170 bom .AddComponent (busyboxComponent )
153171 bom .AddComponent (caCertificatesComponent )
172+ bom .AddComponent (orphanedApkComponent )
154173
155174 return bom
156175 },
@@ -197,6 +216,18 @@ func TestDecoder_Decode_OSPackages(t *testing.T) {
197216 PURL : caCertificatesComponent .PkgIdentifier .PURL ,
198217 },
199218 },
219+ {
220+ ID : "orphaned-package@1.0.0" ,
221+ Name : "orphaned-package" ,
222+ Version : "1.0.0" ,
223+ Arch : "aarch64" ,
224+ SrcName : "orphaned-package" ,
225+ SrcVersion : "1.0.0" ,
226+ Licenses : []string {"MIT" },
227+ Identifier : ftypes.PkgIdentifier {
228+ PURL : orphanedApkComponent .PkgIdentifier .PURL ,
229+ },
230+ },
200231 },
201232 },
202233 },
@@ -233,6 +264,66 @@ func TestDecoder_Decode_OSPackages(t *testing.T) {
233264 },
234265 },
235266 },
267+ {
268+ name : "mixed OS packages (in-graph and out-of-graph) should be merged" ,
269+ setupBOM : func () * core.BOM {
270+ bom := core .NewBOM (core.Options {})
271+ bom .SerialNumber = "test-mixed-os-packages"
272+ bom .Version = 1
273+
274+ // Add OS component
275+ bom .AddComponent (wolfiOSComponent )
276+
277+ // Add OS packages - busybox is connected to OS (in-graph)
278+ bom .AddComponent (busyboxComponent )
279+ // Add orphaned package not connected to OS (out-of-graph)
280+ bom .AddComponent (orphanedApkComponent )
281+
282+ // Create relationship between OS and busybox only
283+ bom .AddRelationship (wolfiOSComponent , busyboxComponent , core .RelationshipContains )
284+ // orphanedApkComponent is not connected to OS component
285+
286+ return bom
287+ },
288+ wantSBOM : types.SBOM {
289+ Metadata : types.Metadata {
290+ OS : & ftypes.OS {
291+ Family : ftypes .Wolfi ,
292+ Name : "20230201" ,
293+ },
294+ },
295+ Packages : []ftypes.PackageInfo {
296+ {
297+ Packages : ftypes.Packages {
298+ {
299+ ID : "busybox@1.37.0-r42" ,
300+ Name : "busybox" ,
301+ Version : "1.37.0-r42" ,
302+ Arch : "aarch64" ,
303+ SrcName : "busybox" ,
304+ SrcVersion : "1.37.0-r42" ,
305+ Licenses : []string {"GPL-2.0-only" },
306+ Identifier : ftypes.PkgIdentifier {
307+ PURL : busyboxComponent .PkgIdentifier .PURL ,
308+ },
309+ },
310+ {
311+ ID : "orphaned-package@1.0.0" ,
312+ Name : "orphaned-package" ,
313+ Version : "1.0.0" ,
314+ Arch : "aarch64" ,
315+ SrcName : "orphaned-package" ,
316+ SrcVersion : "1.0.0" ,
317+ Licenses : []string {"MIT" },
318+ Identifier : ftypes.PkgIdentifier {
319+ PURL : orphanedApkComponent .PkgIdentifier .PURL ,
320+ },
321+ },
322+ },
323+ },
324+ },
325+ },
326+ },
236327 }
237328
238329 for _ , tt := range tests {
@@ -252,7 +343,7 @@ func TestDecoder_Decode_OSPackages(t *testing.T) {
252343 tt .wantSBOM .BOM = gotSBOM .BOM
253344
254345 // Compare the entire SBOM structure
255- assert .Equal (t , tt .wantSBOM , gotSBOM )
346+ assert .EqualExportedValues (t , tt .wantSBOM , gotSBOM )
256347 })
257348 }
258349}
0 commit comments