github.com/google/osv-scalibr@v0.4.1/binary/proto/result_test.go (about)

     1  // Copyright 2025 Google LLC
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //      http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package proto_test
    16  
    17  import (
    18  	"errors"
    19  	"runtime"
    20  	"slices"
    21  	"testing"
    22  	"time"
    23  
    24  	"github.com/docker/docker/api/types/container"
    25  	"github.com/google/go-cmp/cmp"
    26  	"github.com/google/go-cmp/cmp/cmpopts"
    27  	scalibr "github.com/google/osv-scalibr"
    28  	"github.com/google/osv-scalibr/binary/proto"
    29  	"github.com/google/osv-scalibr/extractor"
    30  	ctrdfs "github.com/google/osv-scalibr/extractor/filesystem/containers/containerd"
    31  	"github.com/google/osv-scalibr/extractor/filesystem/containers/podman"
    32  	"github.com/google/osv-scalibr/extractor/filesystem/language/dotnet/depsjson"
    33  	"github.com/google/osv-scalibr/extractor/filesystem/language/java/javalockfile"
    34  	"github.com/google/osv-scalibr/extractor/filesystem/language/java/pomxmlnet"
    35  	"github.com/google/osv-scalibr/extractor/filesystem/language/javascript/packagejson"
    36  	javascriptmeta "github.com/google/osv-scalibr/extractor/filesystem/language/javascript/packagejson/metadata"
    37  	"github.com/google/osv-scalibr/extractor/filesystem/language/python/requirements"
    38  	"github.com/google/osv-scalibr/extractor/filesystem/language/python/wheelegg"
    39  	"github.com/google/osv-scalibr/extractor/filesystem/os/dpkg"
    40  	dpkgmeta "github.com/google/osv-scalibr/extractor/filesystem/os/dpkg/metadata"
    41  	"github.com/google/osv-scalibr/extractor/filesystem/os/homebrew"
    42  	"github.com/google/osv-scalibr/extractor/filesystem/os/nix"
    43  	nixmeta "github.com/google/osv-scalibr/extractor/filesystem/os/nix/metadata"
    44  	"github.com/google/osv-scalibr/extractor/filesystem/os/pacman"
    45  	pacmanmeta "github.com/google/osv-scalibr/extractor/filesystem/os/pacman/metadata"
    46  	"github.com/google/osv-scalibr/extractor/filesystem/os/portage"
    47  	portagemeta "github.com/google/osv-scalibr/extractor/filesystem/os/portage/metadata"
    48  	"github.com/google/osv-scalibr/extractor/filesystem/os/rpm"
    49  	rpmmeta "github.com/google/osv-scalibr/extractor/filesystem/os/rpm/metadata"
    50  	"github.com/google/osv-scalibr/extractor/filesystem/os/winget"
    51  	wingetmeta "github.com/google/osv-scalibr/extractor/filesystem/os/winget/metadata"
    52  	"github.com/google/osv-scalibr/extractor/filesystem/sbom/cdx"
    53  	cdxmeta "github.com/google/osv-scalibr/extractor/filesystem/sbom/cdx/metadata"
    54  	ctrdruntime "github.com/google/osv-scalibr/extractor/standalone/containers/containerd"
    55  	ctrdruntimemd "github.com/google/osv-scalibr/extractor/standalone/containers/containerd/containerdmetadata"
    56  	"github.com/google/osv-scalibr/extractor/standalone/containers/docker"
    57  	winmetadata "github.com/google/osv-scalibr/extractor/standalone/windows/common/metadata"
    58  	"github.com/google/osv-scalibr/extractor/standalone/windows/dismpatch"
    59  	"github.com/google/osv-scalibr/inventory"
    60  	"github.com/google/osv-scalibr/inventory/vex"
    61  	"github.com/google/osv-scalibr/plugin"
    62  	"github.com/google/osv-scalibr/purl"
    63  	"github.com/google/osv-scalibr/veles"
    64  	"github.com/google/osv-scalibr/veles/secrets/gcpsak"
    65  	protobuf "google.golang.org/protobuf/proto"
    66  	"google.golang.org/protobuf/testing/protocmp"
    67  
    68  	spb "github.com/google/osv-scalibr/binary/proto/scan_result_go_proto"
    69  	"google.golang.org/protobuf/types/known/timestamppb"
    70  )
    71  
    72  var (
    73  	purlDPKGAnnotationPackage = &extractor.Package{
    74  		Name:     "software",
    75  		Version:  "1.0.0",
    76  		PURLType: purl.TypeDebian,
    77  		Metadata: &dpkgmeta.Metadata{
    78  			PackageName:       "software",
    79  			PackageVersion:    "1.0.0",
    80  			OSID:              "debian",
    81  			OSVersionCodename: "jammy",
    82  			Maintainer:        "maintainer",
    83  			Architecture:      "amd64",
    84  		},
    85  		Locations: []string{"/file1"},
    86  		Plugins:   []string{dpkg.Name},
    87  		ExploitabilitySignals: []*vex.PackageExploitabilitySignal{&vex.PackageExploitabilitySignal{
    88  			Plugin:          dpkg.Name,
    89  			Justification:   vex.ComponentNotPresent,
    90  			MatchesAllVulns: true,
    91  		}},
    92  	}
    93  	purlDPKGAnnotationPackageProto = &spb.Package{
    94  		Name:    "software",
    95  		Version: "1.0.0",
    96  		Purl: &spb.Purl{
    97  			Purl:      "pkg:deb/debian/software@1.0.0?arch=amd64&distro=jammy",
    98  			Type:      purl.TypeDebian,
    99  			Namespace: "debian",
   100  			Name:      "software",
   101  			Version:   "1.0.0",
   102  			Qualifiers: []*spb.Qualifier{
   103  				{Key: "arch", Value: "amd64"},
   104  				{Key: "distro", Value: "jammy"},
   105  			},
   106  		},
   107  		Ecosystem: "Debian",
   108  		Metadata: &spb.Package_DpkgMetadata{
   109  			DpkgMetadata: &spb.DPKGPackageMetadata{
   110  				PackageName:       "software",
   111  				PackageVersion:    "1.0.0",
   112  				OsId:              "debian",
   113  				OsVersionCodename: "jammy",
   114  				Maintainer:        "maintainer",
   115  				Architecture:      "amd64",
   116  			},
   117  		},
   118  		Locations: []string{"/file1"},
   119  		// TODO(b/400910349): Remove once integrators stop using these fields.
   120  		Plugins: []string{"os/dpkg"},
   121  		ExploitabilitySignals: []*spb.PackageExploitabilitySignal{&spb.PackageExploitabilitySignal{
   122  			Plugin:        dpkg.Name,
   123  			Justification: spb.VexJustification_COMPONENT_NOT_PRESENT,
   124  			VulnFilter:    &spb.PackageExploitabilitySignal_MatchesAllVulns{MatchesAllVulns: true},
   125  		}},
   126  	}
   127  )
   128  
   129  func TestScanResultToProtoAndBack(t *testing.T) {
   130  	endTime := time.Now()
   131  	startTime := endTime.Add(time.Second * -10)
   132  	success := &plugin.ScanStatus{Status: plugin.ScanStatusSucceeded}
   133  	successProto := &spb.ScanStatus{Status: spb.ScanStatus_SUCCEEDED}
   134  	failure := &plugin.ScanStatus{Status: plugin.ScanStatusFailed, FailureReason: "failure"}
   135  	failureProto := &spb.ScanStatus{Status: spb.ScanStatus_FAILED, FailureReason: "failure"}
   136  	purlDPKGPackage := &extractor.Package{
   137  		Name:     "software",
   138  		Version:  "1.0.0",
   139  		PURLType: purl.TypeDebian,
   140  		Metadata: &dpkgmeta.Metadata{
   141  			PackageName:       "software",
   142  			PackageVersion:    "1.0.0",
   143  			OSID:              "debian",
   144  			OSVersionCodename: "jammy",
   145  			Maintainer:        "maintainer",
   146  			Architecture:      "amd64",
   147  		},
   148  		Locations: []string{"/file1"},
   149  		Plugins:   []string{dpkg.Name},
   150  	}
   151  	purlPythonPackage := &extractor.Package{
   152  		Name:      "software",
   153  		Version:   "1.0.0",
   154  		PURLType:  purl.TypePyPi,
   155  		Locations: []string{"/file1"},
   156  		Plugins:   []string{wheelegg.Name},
   157  		Metadata: &wheelegg.PythonPackageMetadata{
   158  			Author:      "author",
   159  			AuthorEmail: "author@corp.com",
   160  		},
   161  	}
   162  	pythonRequirementsPackage := &extractor.Package{
   163  		Name:      "foo",
   164  		Version:   "1.0",
   165  		PURLType:  purl.TypePyPi,
   166  		Locations: []string{"/file1"},
   167  		Plugins:   []string{requirements.Name},
   168  		Metadata: &requirements.Metadata{
   169  			HashCheckingModeValues: []string{"sha256:123"},
   170  			VersionComparator:      ">=",
   171  			Requirement:            "foo>=1.0",
   172  		},
   173  	}
   174  	purlJavascriptPackage := &extractor.Package{
   175  		Name:     "software",
   176  		Version:  "1.0.0",
   177  		PURLType: purl.TypeNPM,
   178  		Metadata: &javascriptmeta.JavascriptPackageJSONMetadata{
   179  			Maintainers: []*javascriptmeta.Person{
   180  				{
   181  					Name:  "maintainer1",
   182  					Email: "maintainer1@corp.com",
   183  					URL:   "https://blog.maintainer1.com",
   184  				},
   185  				{
   186  					Name:  "maintainer2",
   187  					Email: "maintainer2@corp.com",
   188  				},
   189  			},
   190  			Source: javascriptmeta.Unknown,
   191  		},
   192  		Locations: []string{"/file1"},
   193  		Plugins:   []string{packagejson.Name},
   194  	}
   195  
   196  	purlDotnetDepsJSONPackage := &extractor.Package{
   197  		Name:     "software",
   198  		Version:  "1.0.0",
   199  		PURLType: purl.TypeNuget,
   200  		Metadata: &depsjson.Metadata{
   201  			PackageName:    "software",
   202  			PackageVersion: "1.0.0",
   203  			Type:           "type",
   204  		},
   205  		Locations: []string{"/file1"},
   206  		Plugins:   []string{depsjson.Name},
   207  	}
   208  
   209  	purlDotnetDepsJSONPackageProto := &spb.Package{
   210  		Name:    "software",
   211  		Version: "1.0.0",
   212  		Purl: &spb.Purl{
   213  			Purl:    "pkg:nuget/software@1.0.0",
   214  			Type:    purl.TypeNuget,
   215  			Name:    "software",
   216  			Version: "1.0.0",
   217  		},
   218  		Ecosystem: "NuGet",
   219  		Locations: []string{"/file1"},
   220  		Plugins:   []string{"dotnet/depsjson"},
   221  		Metadata: &spb.Package_DepsjsonMetadata{
   222  			DepsjsonMetadata: &spb.DEPSJSONMetadata{
   223  				PackageName:    "software",
   224  				PackageVersion: "1.0.0",
   225  				Type:           "type",
   226  			},
   227  		},
   228  	}
   229  
   230  	windowsPackage := &extractor.Package{
   231  		Name:     "windows_server_2019",
   232  		Version:  "10.0.17763.3406",
   233  		PURLType: "windows",
   234  		Metadata: &winmetadata.OSVersion{
   235  			Product:     "windows_server_2019",
   236  			FullVersion: "10.0.17763.3406",
   237  		},
   238  		Plugins: []string{dismpatch.Name},
   239  	}
   240  
   241  	purlDPKGPackageProto := &spb.Package{
   242  		Name:    "software",
   243  		Version: "1.0.0",
   244  		Purl: &spb.Purl{
   245  			Purl:      "pkg:deb/debian/software@1.0.0?arch=amd64&distro=jammy",
   246  			Type:      purl.TypeDebian,
   247  			Namespace: "debian",
   248  			Name:      "software",
   249  			Version:   "1.0.0",
   250  			Qualifiers: []*spb.Qualifier{
   251  				{Key: "arch", Value: "amd64"},
   252  				{Key: "distro", Value: "jammy"},
   253  			},
   254  		},
   255  		Ecosystem: "Debian",
   256  		Metadata: &spb.Package_DpkgMetadata{
   257  			DpkgMetadata: &spb.DPKGPackageMetadata{
   258  				PackageName:       "software",
   259  				PackageVersion:    "1.0.0",
   260  				OsId:              "debian",
   261  				OsVersionCodename: "jammy",
   262  				Maintainer:        "maintainer",
   263  				Architecture:      "amd64",
   264  			},
   265  		},
   266  		Locations: []string{"/file1"},
   267  		Plugins:   []string{"os/dpkg"},
   268  	}
   269  	purlPythonPackageProto := &spb.Package{
   270  		Name:    "software",
   271  		Version: "1.0.0",
   272  		Purl: &spb.Purl{
   273  			Purl:    "pkg:pypi/software@1.0.0",
   274  			Type:    purl.TypePyPi,
   275  			Name:    "software",
   276  			Version: "1.0.0",
   277  		},
   278  		Ecosystem: "PyPI",
   279  		Locations: []string{"/file1"},
   280  		Plugins:   []string{"python/wheelegg"},
   281  		Metadata: &spb.Package_PythonMetadata{
   282  			PythonMetadata: &spb.PythonPackageMetadata{
   283  				Author:      "author",
   284  				AuthorEmail: "author@corp.com",
   285  			},
   286  		},
   287  	}
   288  	pythonRequirementsPackageProto := &spb.Package{
   289  		Name:    "foo",
   290  		Version: "1.0",
   291  		Purl: &spb.Purl{
   292  			Purl:    "pkg:pypi/foo@1.0",
   293  			Type:    purl.TypePyPi,
   294  			Name:    "foo",
   295  			Version: "1.0",
   296  		},
   297  		Ecosystem: "PyPI",
   298  		Locations: []string{"/file1"},
   299  		Plugins:   []string{"python/requirements"},
   300  		Metadata: &spb.Package_PythonRequirementsMetadata{
   301  			PythonRequirementsMetadata: &spb.PythonRequirementsMetadata{
   302  				HashCheckingModeValues: []string{"sha256:123"},
   303  				VersionComparator:      ">=",
   304  				Requirement:            "foo>=1.0",
   305  			},
   306  		},
   307  	}
   308  	purlJavascriptPackageProto := &spb.Package{
   309  		Name:    "software",
   310  		Version: "1.0.0",
   311  		Purl: &spb.Purl{
   312  			Purl:    "pkg:npm/software@1.0.0",
   313  			Type:    purl.TypeNPM,
   314  			Name:    "software",
   315  			Version: "1.0.0",
   316  		},
   317  		Ecosystem: "npm",
   318  		Locations: []string{"/file1"},
   319  		Plugins:   []string{"javascript/packagejson"},
   320  		Metadata: &spb.Package_JavascriptMetadata{
   321  			JavascriptMetadata: &spb.JavascriptPackageJSONMetadata{
   322  				Source: spb.PackageSource_UNKNOWN,
   323  				Maintainers: []string{
   324  					"maintainer1 <maintainer1@corp.com> (https://blog.maintainer1.com)",
   325  					"maintainer2 <maintainer2@corp.com>",
   326  				},
   327  			},
   328  		},
   329  	}
   330  	cdxPackage := &extractor.Package{
   331  		Name:     "openssl",
   332  		Version:  "1.1.1",
   333  		PURLType: purl.TypeNPM,
   334  		Metadata: &cdxmeta.Metadata{
   335  			PURL: &purl.PackageURL{
   336  				Type:    purl.TypeNPM,
   337  				Name:    "openssl",
   338  				Version: "1.1.1",
   339  			},
   340  		},
   341  		Locations: []string{"/openssl"},
   342  		Plugins:   []string{cdx.Name},
   343  	}
   344  	cdxPackageProto := &spb.Package{
   345  		Name:      "openssl",
   346  		Version:   "1.1.1",
   347  		Ecosystem: "npm",
   348  		Purl: &spb.Purl{
   349  			Purl:    "pkg:npm/openssl@1.1.1",
   350  			Type:    purl.TypeNPM,
   351  			Name:    "openssl",
   352  			Version: "1.1.1",
   353  		},
   354  		Metadata: &spb.Package_CdxMetadata{
   355  			CdxMetadata: &spb.CDXPackageMetadata{
   356  				Purl: &spb.Purl{
   357  					Purl:    "pkg:npm/openssl@1.1.1",
   358  					Type:    purl.TypeNPM,
   359  					Name:    "openssl",
   360  					Version: "1.1.1",
   361  				},
   362  			},
   363  		},
   364  		Locations: []string{"/openssl"},
   365  		Plugins:   []string{"sbom/cdx"},
   366  	}
   367  	purlRPMPackage := &extractor.Package{
   368  		Name:     "openssh-clients",
   369  		Version:  "5.3p1",
   370  		PURLType: purl.TypeRPM,
   371  		Metadata: &rpmmeta.Metadata{
   372  			PackageName:  "openssh-clients",
   373  			SourceRPM:    "openssh-5.3p1-124.el6_10.src.rpm",
   374  			Epoch:        2,
   375  			OSID:         "rhel",
   376  			OSVersionID:  "8.9",
   377  			OSBuildID:    "",
   378  			OSName:       "Red Hat Enterprise Linux",
   379  			Vendor:       "CentOS",
   380  			Architecture: "x86_64",
   381  		},
   382  		Licenses:  []string{"BSD"},
   383  		Locations: []string{"/file1"},
   384  		Plugins:   []string{rpm.Name},
   385  	}
   386  	purlRPMPackageProto := &spb.Package{
   387  		Name:    "openssh-clients",
   388  		Version: "5.3p1",
   389  		Purl: &spb.Purl{
   390  			Purl:      "pkg:rpm/rhel/openssh-clients@5.3p1?arch=x86_64&distro=rhel-8.9&epoch=2&sourcerpm=openssh-5.3p1-124.el6_10.src.rpm",
   391  			Type:      purl.TypeRPM,
   392  			Namespace: "rhel",
   393  			Name:      "openssh-clients",
   394  			Version:   "5.3p1",
   395  			Qualifiers: []*spb.Qualifier{
   396  				{Key: "arch", Value: "x86_64"},
   397  				{Key: "distro", Value: "rhel-8.9"},
   398  				{Key: "epoch", Value: "2"},
   399  				{Key: "sourcerpm", Value: "openssh-5.3p1-124.el6_10.src.rpm"},
   400  			},
   401  		},
   402  		Licenses:  []string{"BSD"},
   403  		Ecosystem: "Red Hat",
   404  		Metadata: &spb.Package_RpmMetadata{
   405  			RpmMetadata: &spb.RPMPackageMetadata{
   406  				PackageName:  "openssh-clients",
   407  				SourceRpm:    "openssh-5.3p1-124.el6_10.src.rpm",
   408  				Epoch:        2,
   409  				OsId:         "rhel",
   410  				OsVersionId:  "8.9",
   411  				OsBuildId:    "",
   412  				OsName:       "Red Hat Enterprise Linux",
   413  				Vendor:       "CentOS",
   414  				Architecture: "x86_64",
   415  			},
   416  		},
   417  		Locations: []string{"/file1"},
   418  		Plugins:   []string{"os/rpm"},
   419  	}
   420  	purlPACMANPackage := &extractor.Package{
   421  		Name:     "zstd",
   422  		Version:  "1.5.6-1",
   423  		PURLType: purl.TypePacman,
   424  		Metadata: &pacmanmeta.Metadata{
   425  			PackageName:    "zstd",
   426  			PackageVersion: "1.5.6-1",
   427  			OSID:           "arch",
   428  			OSVersionID:    "20241201.0.284684",
   429  		},
   430  		Locations: []string{"/file1"},
   431  		Plugins:   []string{pacman.Name},
   432  	}
   433  	purlPACMANPackageProto := &spb.Package{
   434  		Name:    "zstd",
   435  		Version: "1.5.6-1",
   436  		Purl: &spb.Purl{
   437  			Purl:      "pkg:pacman/arch/zstd@1.5.6-1?distro=20241201.0.284684",
   438  			Type:      purl.TypePacman,
   439  			Namespace: "arch",
   440  			Name:      "zstd",
   441  			Version:   "1.5.6-1",
   442  			Qualifiers: []*spb.Qualifier{
   443  				{Key: "distro", Value: "20241201.0.284684"},
   444  			},
   445  		},
   446  		Ecosystem: "",
   447  		Metadata: &spb.Package_PacmanMetadata{
   448  			PacmanMetadata: &spb.PACMANPackageMetadata{
   449  				PackageName:    "zstd",
   450  				PackageVersion: "1.5.6-1",
   451  				OsId:           "arch",
   452  				OsVersionId:    "20241201.0.284684",
   453  			},
   454  		},
   455  		Locations: []string{"/file1"},
   456  		Plugins:   []string{"os/pacman"},
   457  	}
   458  	purlPORTAGEPackage := &extractor.Package{
   459  		Name:     "Capture-Tiny",
   460  		Version:  "0.480.0-r1",
   461  		PURLType: purl.TypePortage,
   462  		Metadata: &portagemeta.Metadata{
   463  			PackageName:    "Capture-Tiny",
   464  			PackageVersion: "0.480.0-r1",
   465  			OSID:           "gentoo",
   466  			OSVersionID:    "2.17",
   467  		},
   468  		Locations: []string{"/file1"},
   469  		Plugins:   []string{portage.Name},
   470  	}
   471  	purlPORTAGEPackageProto := &spb.Package{
   472  		Name:    "Capture-Tiny",
   473  		Version: "0.480.0-r1",
   474  		Purl: &spb.Purl{
   475  			Purl:      "pkg:portage/gentoo/Capture-Tiny@0.480.0-r1?distro=2.17",
   476  			Type:      purl.TypePortage,
   477  			Namespace: "gentoo",
   478  			Name:      "Capture-Tiny",
   479  			Version:   "0.480.0-r1",
   480  			Qualifiers: []*spb.Qualifier{
   481  				{Key: "distro", Value: "2.17"},
   482  			},
   483  		},
   484  		Ecosystem: "",
   485  		Metadata: &spb.Package_PortageMetadata{
   486  			PortageMetadata: &spb.PortagePackageMetadata{
   487  				PackageName:    "Capture-Tiny",
   488  				PackageVersion: "0.480.0-r1",
   489  				OsId:           "gentoo",
   490  				OsVersionId:    "2.17",
   491  			},
   492  		},
   493  		Locations: []string{"/file1"},
   494  		Plugins:   []string{"os/portage"},
   495  	}
   496  	purlNixPackage := &extractor.Package{
   497  		Name:     "attr",
   498  		Version:  "2.5.2",
   499  		PURLType: purl.TypeNix,
   500  		Metadata: &nixmeta.Metadata{
   501  			PackageName:       "attr",
   502  			PackageVersion:    "2.5.2",
   503  			OSID:              "nixos",
   504  			OSVersionCodename: "vicuna",
   505  			OSVersionID:       "24.11",
   506  		},
   507  		Locations: []string{"/file1"},
   508  		Plugins:   []string{nix.Name},
   509  	}
   510  	purlNixPackageProto := &spb.Package{
   511  		Name:    "attr",
   512  		Version: "2.5.2",
   513  		Purl: &spb.Purl{
   514  			Purl:    "pkg:nix/attr@2.5.2?distro=vicuna",
   515  			Type:    purl.TypeNix,
   516  			Name:    "attr",
   517  			Version: "2.5.2",
   518  			Qualifiers: []*spb.Qualifier{
   519  				{Key: "distro", Value: "vicuna"},
   520  			},
   521  		},
   522  		Ecosystem: "",
   523  		Metadata: &spb.Package_NixMetadata{
   524  			NixMetadata: &spb.NixPackageMetadata{
   525  				PackageName:       "attr",
   526  				PackageVersion:    "2.5.2",
   527  				OsId:              "nixos",
   528  				OsVersionCodename: "vicuna",
   529  				OsVersionId:       "24.11",
   530  			},
   531  		},
   532  		Locations: []string{"/file1"},
   533  		Plugins:   []string{"os/nix"},
   534  	}
   535  	purlHomebrewPackage := &extractor.Package{
   536  		Name:      "rclone",
   537  		Version:   "1.67.0",
   538  		PURLType:  purl.TypeBrew,
   539  		Metadata:  &homebrew.Metadata{},
   540  		Locations: []string{"/file1"},
   541  		Plugins:   []string{homebrew.Name},
   542  	}
   543  	purlHomebrewPackageProto := &spb.Package{
   544  		Name:    "rclone",
   545  		Version: "1.67.0",
   546  		Purl: &spb.Purl{
   547  			Purl:    "pkg:brew/rclone@1.67.0",
   548  			Type:    purl.TypeBrew,
   549  			Name:    "rclone",
   550  			Version: "1.67.0",
   551  		},
   552  		Metadata:  &spb.Package_HomebrewMetadata{},
   553  		Locations: []string{"/file1"},
   554  		Plugins:   []string{"os/homebrew"},
   555  	}
   556  	purlWingetPackage := &extractor.Package{
   557  		Name:     "Git.Git",
   558  		Version:  "2.50.1",
   559  		PURLType: purl.TypeWinget,
   560  		Metadata: &wingetmeta.Metadata{
   561  			Name:     "Git",
   562  			ID:       "Git.Git",
   563  			Version:  "2.50.1",
   564  			Moniker:  "git",
   565  			Channel:  "",
   566  			Tags:     []string{"git", "vcs"},
   567  			Commands: []string{"git"},
   568  		},
   569  		Locations: []string{"/file1"},
   570  		Plugins:   []string{winget.Name},
   571  	}
   572  	purlWingetPackageProto := &spb.Package{
   573  		Name:    "Git.Git",
   574  		Version: "2.50.1",
   575  		Purl: &spb.Purl{
   576  			Purl:    "pkg:winget/Git.Git@2.50.1",
   577  			Type:    purl.TypeWinget,
   578  			Name:    "Git.Git",
   579  			Version: "2.50.1",
   580  		},
   581  		Metadata: &spb.Package_WingetMetadata{
   582  			WingetMetadata: &spb.WingetPackageMetadata{
   583  				Name:     "Git",
   584  				Id:       "Git.Git",
   585  				Version:  "2.50.1",
   586  				Moniker:  "git",
   587  				Channel:  "",
   588  				Tags:     []string{"git", "vcs"},
   589  				Commands: []string{"git"},
   590  			},
   591  		},
   592  		Locations: []string{"/file1"},
   593  		Plugins:   []string{"os/winget"},
   594  	}
   595  	containerdPackage := &extractor.Package{
   596  		Name:    "gcr.io/google-samples/hello-app:1.0",
   597  		Version: "sha256:b1455e1c4fcc5ea1023c9e3b584cd84b64eb920e332feff690a2829696e379e7",
   598  		Metadata: &ctrdfs.Metadata{
   599  			Namespace:   "default",
   600  			ImageName:   "gcr.io/google-samples/hello-app:1.0",
   601  			ImageDigest: "sha256:b1455e1c4fcc5ea1023c9e3b584cd84b64eb920e332feff690a2829696e379e7",
   602  			Runtime:     "io.containerd.runc.v2",
   603  			PID:         8915,
   604  			Snapshotter: "overlayfs",
   605  			SnapshotKey: "abcweraweroiuojgawer1",
   606  			LowerDir:    "/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs",
   607  			UpperDir:    "/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/4/fs",
   608  			WorkDir:     "/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/4/work",
   609  		},
   610  		Locations: []string{"/file4"},
   611  		Plugins:   []string{ctrdfs.Name},
   612  	}
   613  	containerdPackageProto := &spb.Package{
   614  		Name:      "gcr.io/google-samples/hello-app:1.0",
   615  		Version:   "sha256:b1455e1c4fcc5ea1023c9e3b584cd84b64eb920e332feff690a2829696e379e7",
   616  		Ecosystem: "",
   617  		Metadata: &spb.Package_ContainerdContainerMetadata{
   618  			ContainerdContainerMetadata: &spb.ContainerdContainerMetadata{
   619  				NamespaceName: "default",
   620  				ImageName:     "gcr.io/google-samples/hello-app:1.0",
   621  				ImageDigest:   "sha256:b1455e1c4fcc5ea1023c9e3b584cd84b64eb920e332feff690a2829696e379e7",
   622  				Runtime:       "io.containerd.runc.v2",
   623  				Pid:           8915,
   624  				Snapshotter:   "overlayfs",
   625  				SnapshotKey:   "abcweraweroiuojgawer1",
   626  				LowerDir:      "/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/1/fs",
   627  				UpperDir:      "/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/4/fs",
   628  				WorkDir:       "/var/lib/containerd/io.containerd.snapshotter.v1.overlayfs/snapshots/4/work",
   629  			},
   630  		},
   631  		Locations: []string{"/file4"},
   632  		Plugins:   []string{"containers/containerd"},
   633  	}
   634  	containerdRuntimePackage := &extractor.Package{
   635  		Name:    "gcr.io/google-samples/hello-app:1.0",
   636  		Version: "sha256:b1455e1c4fcc5ea1023c9e3b584cd84b64eb920e332feff690a2829696e379e7",
   637  		Metadata: &ctrdruntimemd.Metadata{
   638  			Namespace:   "default",
   639  			ImageName:   "gcr.io/google-samples/hello-app:1.0",
   640  			ImageDigest: "sha256:b1455e1c4fcc5ea1023c9e3b584cd84b64eb920e332feff690a2829696e379e7",
   641  			Runtime:     "io.containerd.runc.v2",
   642  			ID:          "1234567890",
   643  			PID:         8915,
   644  			RootFS:      "/run/containerd/io.containerd.runtime.v2.task/default/1234567890/rootfs",
   645  		},
   646  		Locations: []string{"/file7"},
   647  		Plugins:   []string{ctrdruntime.Name},
   648  	}
   649  	containerdRuntimePackageProto := &spb.Package{
   650  		Name:      "gcr.io/google-samples/hello-app:1.0",
   651  		Version:   "sha256:b1455e1c4fcc5ea1023c9e3b584cd84b64eb920e332feff690a2829696e379e7",
   652  		Ecosystem: "",
   653  		Metadata: &spb.Package_ContainerdRuntimeContainerMetadata{
   654  			ContainerdRuntimeContainerMetadata: &spb.ContainerdRuntimeContainerMetadata{
   655  				NamespaceName: "default",
   656  				ImageName:     "gcr.io/google-samples/hello-app:1.0",
   657  				ImageDigest:   "sha256:b1455e1c4fcc5ea1023c9e3b584cd84b64eb920e332feff690a2829696e379e7",
   658  				Runtime:       "io.containerd.runc.v2",
   659  				Id:            "1234567890",
   660  				Pid:           8915,
   661  				RootfsPath:    "/run/containerd/io.containerd.runtime.v2.task/default/1234567890/rootfs",
   662  			},
   663  		},
   664  		Locations: []string{"/file7"},
   665  		Plugins:   []string{"containers/containerd-runtime"},
   666  	}
   667  	windowsPackageProto := &spb.Package{
   668  		Name:    "windows_server_2019",
   669  		Version: "10.0.17763.3406",
   670  		Metadata: &spb.Package_WindowsOsVersionMetadata{
   671  			WindowsOsVersionMetadata: &spb.WindowsOSVersion{
   672  				Product:     "windows_server_2019",
   673  				FullVersion: "10.0.17763.3406",
   674  			},
   675  		},
   676  		Purl: &spb.Purl{
   677  			Purl:      "pkg:generic/microsoft/windows_server_2019@10.0.17763.3406?buildnumber=10.0.17763.3406",
   678  			Type:      purl.TypeGeneric,
   679  			Namespace: "microsoft",
   680  			Name:      "windows_server_2019",
   681  			Version:   "10.0.17763.3406",
   682  			Qualifiers: []*spb.Qualifier{
   683  				{
   684  					Key:   "buildnumber",
   685  					Value: "10.0.17763.3406",
   686  				},
   687  			},
   688  		},
   689  		Plugins: []string{"windows/dismpatch"},
   690  	}
   691  	mavenPackage := &extractor.Package{
   692  		Name:      "abc:xyz",
   693  		Version:   "1.0.0",
   694  		PURLType:  purl.TypeMaven,
   695  		Locations: []string{"/pom.xml"},
   696  		Plugins:   []string{pomxmlnet.Name},
   697  		Metadata: &javalockfile.Metadata{
   698  			GroupID:      "abc",
   699  			ArtifactID:   "xyz",
   700  			IsTransitive: true,
   701  		},
   702  	}
   703  	mavenPackageProto := &spb.Package{
   704  		Name:      "abc:xyz",
   705  		Version:   "1.0.0",
   706  		Ecosystem: "Maven",
   707  		Purl: &spb.Purl{
   708  			Purl:      "pkg:maven/abc/xyz@1.0.0",
   709  			Type:      purl.TypeMaven,
   710  			Name:      "xyz",
   711  			Namespace: "abc",
   712  			Version:   "1.0.0",
   713  		},
   714  		Locations: []string{"/pom.xml"},
   715  		Plugins:   []string{"java/pomxmlnet"},
   716  		Metadata: &spb.Package_JavaLockfileMetadata{
   717  			JavaLockfileMetadata: &spb.JavaLockfileMetadata{
   718  				ArtifactId:   "xyz",
   719  				GroupId:      "abc",
   720  				IsTransitive: true,
   721  			},
   722  		},
   723  	}
   724  
   725  	podmanPackage := &extractor.Package{
   726  		Name:    "docker.io/redis",
   727  		Version: "a8036f14f15ead9517115576fb4462894a000620c2be556410f6c24afb8a482b",
   728  		Metadata: &podman.Metadata{
   729  			ExposedPorts: map[uint16][]string{6379: {"tcp"}},
   730  			PID:          4232,
   731  			Status:       "running",
   732  			NameSpace:    "",
   733  			StartedTime:  endTime.Add(-10 * time.Minute),
   734  			FinishedTime: endTime.Add(-5 * time.Minute),
   735  			ExitCode:     0,
   736  			Exited:       false,
   737  		},
   738  		Plugins: []string{podman.Name},
   739  	}
   740  	podmanPackageProto := &spb.Package{
   741  		Name:    "docker.io/redis",
   742  		Version: "a8036f14f15ead9517115576fb4462894a000620c2be556410f6c24afb8a482b",
   743  		Metadata: &spb.Package_PodmanMetadata{
   744  			PodmanMetadata: &spb.PodmanMetadata{
   745  				ExposedPorts:  map[uint32]*spb.Protocol{6379: {Names: []string{"tcp"}}},
   746  				Pid:           4232,
   747  				NamespaceName: "",
   748  				StartedTime:   timestamppb.New(endTime.Add(-10 * time.Minute)),
   749  				FinishedTime:  timestamppb.New(endTime.Add(-5 * time.Minute)),
   750  				Status:        "running",
   751  				ExitCode:      0,
   752  				Exited:        false,
   753  			},
   754  		},
   755  		Plugins: []string{"containers/podman"},
   756  	}
   757  
   758  	gcpsakSecret := &inventory.Secret{
   759  		Secret: gcpsak.GCPSAK{
   760  			PrivateKeyID:   "some-private-key-id",
   761  			ServiceAccount: "some-service-account@gserviceaccount.iam.google.com",
   762  			Signature:      make([]byte, 256),
   763  		},
   764  		Location: "/foo/bar/baz.json",
   765  		Validation: inventory.SecretValidationResult{
   766  			At:     startTime,
   767  			Status: veles.ValidationInvalid,
   768  		},
   769  	}
   770  	gcpsakSecretProto := &spb.Secret{
   771  		Secret: &spb.SecretData{
   772  			Secret: &spb.SecretData_Gcpsak{
   773  				Gcpsak: &spb.SecretData_GCPSAK{
   774  					PrivateKeyId: "some-private-key-id",
   775  					ClientEmail:  "some-service-account@gserviceaccount.iam.google.com",
   776  					Signature:    make([]byte, 256),
   777  				},
   778  			},
   779  		},
   780  		Status: &spb.SecretStatus{
   781  			Status:      spb.SecretStatus_INVALID,
   782  			LastUpdated: timestamppb.New(startTime),
   783  		},
   784  		Locations: []*spb.Location{
   785  			&spb.Location{
   786  				Location: &spb.Location_Filepath{
   787  					Filepath: &spb.Filepath{
   788  						Path: "/foo/bar/baz.json",
   789  					},
   790  				},
   791  			},
   792  		},
   793  	}
   794  	gcpsakSecretWithExtra := &inventory.Secret{
   795  		Secret: gcpsak.GCPSAK{
   796  			PrivateKeyID:   "some-private-key-id",
   797  			ServiceAccount: "some-service-account@gserviceaccount.iam.google.com",
   798  			Signature:      make([]byte, 256),
   799  			Extra: &gcpsak.ExtraFields{
   800  				Type:                    "service_account",
   801  				ProjectID:               "some-project-id",
   802  				ClientID:                "1122334455",
   803  				AuthURI:                 "https://accounts.google.com/o/oauth2/auth",
   804  				TokenURI:                "https://oauth2.googleapis.com/token",
   805  				AuthProviderX509CertURL: "https://www.googleapis.com/oauth2/v1/certs",
   806  				ClientX509CertURL:       "https://www.googleapis.com/robot/v1/metadata/x509/some-service-account%40gserviceaccount.iam.google.com",
   807  				UniverseDomain:          "googleapis.com",
   808  				PrivateKey:              "-----BEGIN PRIVATE KEY-----\nREDACTED\n-----END PRIVATE KEY-----\n",
   809  			},
   810  		},
   811  		Location: "/foo/bar/baz.json",
   812  		Validation: inventory.SecretValidationResult{
   813  			At:     startTime,
   814  			Status: veles.ValidationInvalid,
   815  		},
   816  	}
   817  	gcpsakSecretProtoWithExtra := &spb.Secret{
   818  		Secret: &spb.SecretData{
   819  			Secret: &spb.SecretData_Gcpsak{
   820  				Gcpsak: &spb.SecretData_GCPSAK{
   821  					PrivateKeyId:            "some-private-key-id",
   822  					ClientEmail:             "some-service-account@gserviceaccount.iam.google.com",
   823  					Signature:               make([]byte, 256),
   824  					Type:                    "service_account",
   825  					ProjectId:               "some-project-id",
   826  					ClientId:                "1122334455",
   827  					AuthUri:                 "https://accounts.google.com/o/oauth2/auth",
   828  					TokenUri:                "https://oauth2.googleapis.com/token",
   829  					AuthProviderX509CertUrl: "https://www.googleapis.com/oauth2/v1/certs",
   830  					ClientX509CertUrl:       "https://www.googleapis.com/robot/v1/metadata/x509/some-service-account%40gserviceaccount.iam.google.com",
   831  					UniverseDomain:          "googleapis.com",
   832  					PrivateKey:              "-----BEGIN PRIVATE KEY-----\nREDACTED\n-----END PRIVATE KEY-----\n",
   833  				},
   834  			},
   835  		},
   836  		Status: &spb.SecretStatus{
   837  			Status:      spb.SecretStatus_INVALID,
   838  			LastUpdated: timestamppb.New(startTime),
   839  		},
   840  		Locations: []*spb.Location{
   841  			&spb.Location{
   842  				Location: &spb.Location_Filepath{
   843  					Filepath: &spb.Filepath{
   844  						Path: "/foo/bar/baz.json",
   845  					},
   846  				},
   847  			},
   848  		},
   849  	}
   850  
   851  	dockerPackage := &extractor.Package{
   852  		Name:    "redis",
   853  		Version: "sha256:a8036f14f15ead9517115576fb4462894a000620c2be556410f6c24afb8a482b",
   854  		Metadata: &docker.Metadata{
   855  			ImageName:   "redis",
   856  			ImageDigest: "sha256:a8036f14f15ead9517115576fb4462894a000620c2be556410f6c24afb8a482b",
   857  			ID:          "3ea6adad2e94daf386e1d6c5960807b41f19da2333e8a6261065c1cb8e85ac81",
   858  			Ports:       []container.Port{{IP: "127.0.0.1", PrivatePort: 6379, PublicPort: 1112, Type: "tcp"}},
   859  		},
   860  		Plugins: []string{docker.Name},
   861  	}
   862  
   863  	dockerPackageProto := &spb.Package{
   864  		Name:    "redis",
   865  		Version: "sha256:a8036f14f15ead9517115576fb4462894a000620c2be556410f6c24afb8a482b",
   866  		Metadata: &spb.Package_DockerContainersMetadata{
   867  			DockerContainersMetadata: &spb.DockerContainersMetadata{
   868  				ImageName:   "redis",
   869  				ImageDigest: "sha256:a8036f14f15ead9517115576fb4462894a000620c2be556410f6c24afb8a482b",
   870  				Id:          "3ea6adad2e94daf386e1d6c5960807b41f19da2333e8a6261065c1cb8e85ac81",
   871  				Ports: []*spb.DockerPort{
   872  					{Ip: "127.0.0.1", PrivatePort: 6379, PublicPort: 1112, Type: "tcp"},
   873  				},
   874  			},
   875  		},
   876  		Plugins: []string{"containers/docker"},
   877  	}
   878  
   879  	licensePackage := &extractor.Package{
   880  		Name:     "express",
   881  		Version:  "4.17.1",
   882  		Licenses: []string{"MIT"},
   883  	}
   884  	licensePackageProto := &spb.Package{
   885  		Name:     "express",
   886  		Version:  "4.17.1",
   887  		Licenses: []string{"MIT"},
   888  	}
   889  
   890  	testCases := []struct {
   891  		desc         string
   892  		res          *scalibr.ScanResult
   893  		want         *spb.ScanResult
   894  		wantErr      error
   895  		excludeForOS []string // skip test for these operating systems
   896  	}{
   897  		{
   898  			desc: "Successful_scan",
   899  			res: &scalibr.ScanResult{
   900  				Version:   "1.0.0",
   901  				StartTime: startTime,
   902  				EndTime:   endTime,
   903  				Status:    success,
   904  				PluginStatus: []*plugin.Status{
   905  					{
   906  						Name:    "ext",
   907  						Version: 2,
   908  						Status:  success,
   909  					},
   910  					{
   911  						Name:    "det",
   912  						Version: 3,
   913  						Status:  success,
   914  					},
   915  				},
   916  				Inventory: inventory.Inventory{
   917  					Packages: []*extractor.Package{
   918  						purlDPKGPackage,
   919  						purlDPKGAnnotationPackage,
   920  						purlPythonPackage,
   921  						pythonRequirementsPackage,
   922  						purlJavascriptPackage,
   923  						purlDotnetDepsJSONPackage,
   924  						cdxPackage,
   925  						windowsPackage,
   926  						purlHomebrewPackage,
   927  						pkgWithLayerStruct,
   928  					},
   929  					ContainerImageMetadata: []*extractor.ContainerImageMetadata{
   930  						cimStructForTest,
   931  					},
   932  					GenericFindings: []*inventory.GenericFinding{
   933  						{
   934  							Adv: &inventory.GenericFindingAdvisory{
   935  								ID: &inventory.AdvisoryID{
   936  									Publisher: "CVE",
   937  									Reference: "CVE-1234",
   938  								},
   939  								Title:          "Title",
   940  								Description:    "Description",
   941  								Recommendation: "Recommendation",
   942  								Sev:            inventory.SeverityMedium,
   943  							},
   944  							Target: &inventory.GenericFindingTargetDetails{
   945  								Extra: "extra details",
   946  							},
   947  							Plugins: []string{"cve/cve-1234-finder"},
   948  							ExploitabilitySignals: []*vex.FindingExploitabilitySignal{{
   949  								Plugin:        dpkg.Name,
   950  								Justification: vex.ComponentNotPresent,
   951  							}},
   952  						},
   953  					},
   954  				},
   955  			},
   956  			want: &spb.ScanResult{
   957  				Version:   "1.0.0",
   958  				StartTime: timestamppb.New(startTime),
   959  				EndTime:   timestamppb.New(endTime),
   960  				Status:    successProto,
   961  				PluginStatus: []*spb.PluginStatus{
   962  					{
   963  						Name:    "ext",
   964  						Version: 2,
   965  						Status:  successProto,
   966  					},
   967  					{
   968  						Name:    "det",
   969  						Version: 3,
   970  						Status:  successProto,
   971  					},
   972  				},
   973  				Inventory: &spb.Inventory{
   974  					Packages: []*spb.Package{
   975  						purlDPKGPackageProto,
   976  						purlDPKGAnnotationPackageProto,
   977  						purlPythonPackageProto,
   978  						pythonRequirementsPackageProto,
   979  						purlJavascriptPackageProto,
   980  						purlDotnetDepsJSONPackageProto,
   981  						cdxPackageProto,
   982  						windowsPackageProto,
   983  						purlHomebrewPackageProto,
   984  						pkgWithLayerProto,
   985  					},
   986  					ContainerImageMetadata: []*spb.ContainerImageMetadata{
   987  						cimProtoForTest,
   988  					},
   989  					GenericFindings: []*spb.GenericFinding{
   990  						{
   991  							Adv: &spb.GenericFindingAdvisory{
   992  								Id: &spb.AdvisoryId{
   993  									Publisher: "CVE",
   994  									Reference: "CVE-1234",
   995  								},
   996  								Title:          "Title",
   997  								Description:    "Description",
   998  								Recommendation: "Recommendation",
   999  								Sev:            spb.SeverityEnum_MEDIUM,
  1000  							},
  1001  							Target: &spb.GenericFindingTargetDetails{
  1002  								Extra: "extra details",
  1003  							},
  1004  							Plugins: []string{"cve/cve-1234-finder"},
  1005  							ExploitabilitySignals: []*spb.FindingExploitabilitySignal{{
  1006  								Plugin:        dpkg.Name,
  1007  								Justification: spb.VexJustification_COMPONENT_NOT_PRESENT,
  1008  							}},
  1009  						},
  1010  					},
  1011  				},
  1012  			},
  1013  		},
  1014  		{
  1015  			desc: "Successful_RPM_scan_linux-only",
  1016  			res: &scalibr.ScanResult{
  1017  				Version:   "1.0.0",
  1018  				StartTime: startTime,
  1019  				EndTime:   endTime,
  1020  				Status:    success,
  1021  				PluginStatus: []*plugin.Status{
  1022  					{
  1023  						Name:    "ext",
  1024  						Version: 2,
  1025  						Status:  success,
  1026  					},
  1027  				},
  1028  				Inventory: inventory.Inventory{
  1029  					Packages: []*extractor.Package{purlRPMPackage},
  1030  				},
  1031  			},
  1032  			want: &spb.ScanResult{
  1033  				Version:   "1.0.0",
  1034  				StartTime: timestamppb.New(startTime),
  1035  				EndTime:   timestamppb.New(endTime),
  1036  				Status:    successProto,
  1037  				PluginStatus: []*spb.PluginStatus{
  1038  					{
  1039  						Name:    "ext",
  1040  						Version: 2,
  1041  						Status:  successProto,
  1042  					},
  1043  				},
  1044  				Inventory: &spb.Inventory{
  1045  					Packages:        []*spb.Package{purlRPMPackageProto},
  1046  					GenericFindings: []*spb.GenericFinding{},
  1047  				},
  1048  			},
  1049  			excludeForOS: []string{"windows", "darwin"},
  1050  		},
  1051  		{
  1052  			desc: "Successful_PACMAN_scan_linux-only",
  1053  			res: &scalibr.ScanResult{
  1054  				Version:   "1.0.0",
  1055  				StartTime: startTime,
  1056  				EndTime:   endTime,
  1057  				Status:    success,
  1058  				PluginStatus: []*plugin.Status{
  1059  					{
  1060  						Name:    "ext",
  1061  						Version: 2,
  1062  						Status:  success,
  1063  					},
  1064  				},
  1065  				Inventory: inventory.Inventory{
  1066  					Packages: []*extractor.Package{purlPACMANPackage},
  1067  				},
  1068  			},
  1069  			want: &spb.ScanResult{
  1070  				Version:   "1.0.0",
  1071  				StartTime: timestamppb.New(startTime),
  1072  				EndTime:   timestamppb.New(endTime),
  1073  				Status:    successProto,
  1074  				PluginStatus: []*spb.PluginStatus{
  1075  					{
  1076  						Name:    "ext",
  1077  						Version: 2,
  1078  						Status:  successProto,
  1079  					},
  1080  				},
  1081  				Inventory: &spb.Inventory{
  1082  					Packages:        []*spb.Package{purlPACMANPackageProto},
  1083  					GenericFindings: []*spb.GenericFinding{},
  1084  				},
  1085  			},
  1086  			excludeForOS: []string{"windows", "darwin"},
  1087  		},
  1088  		{
  1089  			desc: "Successful_PORTAGE_scan_linux-only",
  1090  			res: &scalibr.ScanResult{
  1091  				Version:   "1.0.0",
  1092  				StartTime: startTime,
  1093  				EndTime:   endTime,
  1094  				Status:    success,
  1095  				PluginStatus: []*plugin.Status{
  1096  					{
  1097  						Name:    "ext",
  1098  						Version: 2,
  1099  						Status:  success,
  1100  					},
  1101  				},
  1102  				Inventory: inventory.Inventory{
  1103  					Packages: []*extractor.Package{purlPORTAGEPackage},
  1104  				},
  1105  			},
  1106  			want: &spb.ScanResult{
  1107  				Version:   "1.0.0",
  1108  				StartTime: timestamppb.New(startTime),
  1109  				EndTime:   timestamppb.New(endTime),
  1110  				Status:    successProto,
  1111  				PluginStatus: []*spb.PluginStatus{
  1112  					{
  1113  						Name:    "ext",
  1114  						Version: 2,
  1115  						Status:  successProto,
  1116  					},
  1117  				},
  1118  				Inventory: &spb.Inventory{
  1119  					Packages:        []*spb.Package{purlPORTAGEPackageProto},
  1120  					GenericFindings: []*spb.GenericFinding{},
  1121  				},
  1122  			},
  1123  			excludeForOS: []string{"windows", "darwin"},
  1124  		},
  1125  		{
  1126  			desc: "Successful_Nix_scan_linux-only",
  1127  			res: &scalibr.ScanResult{
  1128  				Version:   "1.0.0",
  1129  				StartTime: startTime,
  1130  				EndTime:   endTime,
  1131  				Status:    success,
  1132  				PluginStatus: []*plugin.Status{
  1133  					{
  1134  						Name:    "ext",
  1135  						Version: 2,
  1136  						Status:  success,
  1137  					},
  1138  				},
  1139  				Inventory: inventory.Inventory{
  1140  					Packages: []*extractor.Package{purlNixPackage},
  1141  				},
  1142  			},
  1143  			want: &spb.ScanResult{
  1144  				Version:   "1.0.0",
  1145  				StartTime: timestamppb.New(startTime),
  1146  				EndTime:   timestamppb.New(endTime),
  1147  				Status:    successProto,
  1148  				PluginStatus: []*spb.PluginStatus{
  1149  					{
  1150  						Name:    "ext",
  1151  						Version: 2,
  1152  						Status:  successProto,
  1153  					},
  1154  				},
  1155  				Inventory: &spb.Inventory{
  1156  					Packages:        []*spb.Package{purlNixPackageProto},
  1157  					GenericFindings: []*spb.GenericFinding{},
  1158  				},
  1159  			},
  1160  			excludeForOS: []string{"windows", "darwin"},
  1161  		},
  1162  		{
  1163  			desc: "Successful_Homebrew_scan_darwin-only",
  1164  			res: &scalibr.ScanResult{
  1165  				Version:   "1.0.0",
  1166  				StartTime: startTime,
  1167  				EndTime:   endTime,
  1168  				Status:    success,
  1169  				PluginStatus: []*plugin.Status{
  1170  					{
  1171  						Name:    "ext",
  1172  						Version: 2,
  1173  						Status:  success,
  1174  					},
  1175  				},
  1176  				Inventory: inventory.Inventory{
  1177  					Packages: []*extractor.Package{purlHomebrewPackage},
  1178  				},
  1179  			},
  1180  			want: &spb.ScanResult{
  1181  				Version:   "1.0.0",
  1182  				StartTime: timestamppb.New(startTime),
  1183  				EndTime:   timestamppb.New(endTime),
  1184  				Status:    successProto,
  1185  				PluginStatus: []*spb.PluginStatus{
  1186  					{
  1187  						Name:    "ext",
  1188  						Version: 2,
  1189  						Status:  successProto,
  1190  					},
  1191  				},
  1192  				Inventory: &spb.Inventory{
  1193  					Packages:        []*spb.Package{purlHomebrewPackageProto},
  1194  					GenericFindings: []*spb.GenericFinding{},
  1195  				},
  1196  			},
  1197  			excludeForOS: []string{"windows", "linux"},
  1198  		},
  1199  		{
  1200  			desc: "Successful_winget_scan_windows-only",
  1201  			res: &scalibr.ScanResult{
  1202  				Version:   "1.0.0",
  1203  				StartTime: startTime,
  1204  				EndTime:   endTime,
  1205  				Status:    success,
  1206  				PluginStatus: []*plugin.Status{
  1207  					{
  1208  						Name:    "ext",
  1209  						Version: 2,
  1210  						Status:  success,
  1211  					},
  1212  				},
  1213  				Inventory: inventory.Inventory{
  1214  					Packages: []*extractor.Package{purlWingetPackage},
  1215  				},
  1216  			},
  1217  			want: &spb.ScanResult{
  1218  				Version:   "1.0.0",
  1219  				StartTime: timestamppb.New(startTime),
  1220  				EndTime:   timestamppb.New(endTime),
  1221  				Status:    successProto,
  1222  				PluginStatus: []*spb.PluginStatus{
  1223  					{
  1224  						Name:    "ext",
  1225  						Version: 2,
  1226  						Status:  successProto,
  1227  					},
  1228  				},
  1229  				Inventory: &spb.Inventory{
  1230  					Packages:        []*spb.Package{purlWingetPackageProto},
  1231  					GenericFindings: []*spb.GenericFinding{},
  1232  				},
  1233  			},
  1234  			excludeForOS: []string{"darwin", "linux"},
  1235  		},
  1236  		{
  1237  			desc: "Successful_containerd_scan_linux-only",
  1238  			res: &scalibr.ScanResult{
  1239  				Version:   "1.0.0",
  1240  				StartTime: startTime,
  1241  				EndTime:   endTime,
  1242  				Status:    success,
  1243  				PluginStatus: []*plugin.Status{
  1244  					{
  1245  						Name:    "ext",
  1246  						Version: 2,
  1247  						Status:  success,
  1248  					},
  1249  				},
  1250  				Inventory: inventory.Inventory{
  1251  					Packages: []*extractor.Package{containerdPackage},
  1252  				},
  1253  			},
  1254  			want: &spb.ScanResult{
  1255  				Version:   "1.0.0",
  1256  				StartTime: timestamppb.New(startTime),
  1257  				EndTime:   timestamppb.New(endTime),
  1258  				Status:    successProto,
  1259  				PluginStatus: []*spb.PluginStatus{
  1260  					{
  1261  						Name:    "ext",
  1262  						Version: 2,
  1263  						Status:  successProto,
  1264  					},
  1265  				},
  1266  				Inventory: &spb.Inventory{
  1267  					Packages:        []*spb.Package{containerdPackageProto},
  1268  					GenericFindings: []*spb.GenericFinding{},
  1269  				},
  1270  			},
  1271  			// TODO(b/349138656): Remove windows from this exclusion when containerd is supported
  1272  			// on Windows.
  1273  			excludeForOS: []string{"windows", "darwin"},
  1274  		},
  1275  		{
  1276  			desc: "Successful_containerd_runtime_scan_linux-only",
  1277  			res: &scalibr.ScanResult{
  1278  				Version:   "1.0.0",
  1279  				StartTime: startTime,
  1280  				EndTime:   endTime,
  1281  				Status:    success,
  1282  				PluginStatus: []*plugin.Status{
  1283  					{
  1284  						Name:    "ext",
  1285  						Version: 2,
  1286  						Status:  success,
  1287  					},
  1288  				},
  1289  				Inventory: inventory.Inventory{
  1290  					Packages: []*extractor.Package{containerdRuntimePackage},
  1291  				},
  1292  			},
  1293  			want: &spb.ScanResult{
  1294  				Version:   "1.0.0",
  1295  				StartTime: timestamppb.New(startTime),
  1296  				EndTime:   timestamppb.New(endTime),
  1297  				Status:    successProto,
  1298  				PluginStatus: []*spb.PluginStatus{
  1299  					{
  1300  						Name:    "ext",
  1301  						Version: 2,
  1302  						Status:  successProto,
  1303  					},
  1304  				},
  1305  				Inventory: &spb.Inventory{
  1306  					Packages:        []*spb.Package{containerdRuntimePackageProto},
  1307  					GenericFindings: []*spb.GenericFinding{},
  1308  				},
  1309  			},
  1310  			// TODO(b/349138656): Remove windows from this exclusion when containerd is supported
  1311  			// on Windows.
  1312  			excludeForOS: []string{"windows", "darwin"},
  1313  		},
  1314  		{
  1315  			desc: "Successful_docker_scan",
  1316  			res: &scalibr.ScanResult{
  1317  				Version:   "1.0.0",
  1318  				StartTime: startTime,
  1319  				EndTime:   endTime,
  1320  				Status:    success,
  1321  				PluginStatus: []*plugin.Status{
  1322  					{
  1323  						Name:    "ext",
  1324  						Version: 2,
  1325  						Status:  success,
  1326  					},
  1327  				},
  1328  				Inventory: inventory.Inventory{
  1329  					Packages: []*extractor.Package{dockerPackage},
  1330  				},
  1331  			},
  1332  			want: &spb.ScanResult{
  1333  				Version:   "1.0.0",
  1334  				StartTime: timestamppb.New(startTime),
  1335  				EndTime:   timestamppb.New(endTime),
  1336  				Status:    successProto,
  1337  				PluginStatus: []*spb.PluginStatus{
  1338  					{
  1339  						Name:    "ext",
  1340  						Version: 2,
  1341  						Status:  successProto,
  1342  					},
  1343  				},
  1344  				Inventory: &spb.Inventory{
  1345  					Packages:        []*spb.Package{dockerPackageProto},
  1346  					GenericFindings: []*spb.GenericFinding{},
  1347  				},
  1348  			},
  1349  		},
  1350  		{
  1351  			desc: "Successful_podman_runtime_scan_linux-only",
  1352  			res: &scalibr.ScanResult{
  1353  				Version:   "1.0.0",
  1354  				StartTime: startTime,
  1355  				EndTime:   endTime,
  1356  				Status:    success,
  1357  				PluginStatus: []*plugin.Status{
  1358  					{
  1359  						Name:    "ext",
  1360  						Version: 2,
  1361  						Status:  success,
  1362  					},
  1363  				},
  1364  				Inventory: inventory.Inventory{
  1365  					Packages: []*extractor.Package{podmanPackage},
  1366  				},
  1367  			},
  1368  			want: &spb.ScanResult{
  1369  				Version:   "1.0.0",
  1370  				StartTime: timestamppb.New(startTime),
  1371  				EndTime:   timestamppb.New(endTime),
  1372  				Status:    successProto,
  1373  				PluginStatus: []*spb.PluginStatus{
  1374  					{
  1375  						Name:    "ext",
  1376  						Version: 2,
  1377  						Status:  successProto,
  1378  					},
  1379  				},
  1380  				Inventory: &spb.Inventory{
  1381  					Packages:        []*spb.Package{podmanPackageProto},
  1382  					GenericFindings: []*spb.GenericFinding{},
  1383  				},
  1384  			},
  1385  			excludeForOS: []string{"windows", "darwin"},
  1386  		},
  1387  		{
  1388  			desc: "advisory_without_id,_should_error",
  1389  			res: &scalibr.ScanResult{
  1390  				Version:   "1.0.0",
  1391  				StartTime: startTime,
  1392  				EndTime:   endTime,
  1393  				Status:    success,
  1394  				PluginStatus: []*plugin.Status{
  1395  					{
  1396  						Name:    "ext",
  1397  						Version: 2,
  1398  						Status:  success,
  1399  					},
  1400  					{
  1401  						Name:    "det",
  1402  						Version: 3,
  1403  						Status:  success,
  1404  					},
  1405  				},
  1406  				Inventory: inventory.Inventory{
  1407  					Packages: []*extractor.Package{purlDPKGPackage, purlPythonPackage, purlJavascriptPackage, cdxPackage},
  1408  					GenericFindings: []*inventory.GenericFinding{
  1409  						{
  1410  							Adv: &inventory.GenericFindingAdvisory{
  1411  								Title:          "Title",
  1412  								Description:    "Description",
  1413  								Recommendation: "Recommendation",
  1414  								Sev:            inventory.SeverityMedium,
  1415  							},
  1416  							Target: &inventory.GenericFindingTargetDetails{
  1417  								Extra: "extra details",
  1418  							},
  1419  						},
  1420  					},
  1421  				},
  1422  			},
  1423  			wantErr: proto.ErrAdvisoryIDMissing,
  1424  		},
  1425  		{
  1426  			desc: "no_advisory,_should_error",
  1427  			res: &scalibr.ScanResult{
  1428  				Version:   "1.0.0",
  1429  				StartTime: startTime,
  1430  				EndTime:   endTime,
  1431  				Status:    success,
  1432  				PluginStatus: []*plugin.Status{
  1433  					{
  1434  						Name:    "ext",
  1435  						Version: 2,
  1436  						Status:  success,
  1437  					},
  1438  					{
  1439  						Name:    "det",
  1440  						Version: 3,
  1441  						Status:  success,
  1442  					},
  1443  				},
  1444  				Inventory: inventory.Inventory{
  1445  					Packages: []*extractor.Package{purlDPKGPackage, purlPythonPackage, purlJavascriptPackage, cdxPackage},
  1446  					GenericFindings: []*inventory.GenericFinding{
  1447  						{
  1448  							Target: &inventory.GenericFindingTargetDetails{
  1449  								Extra: "extra details",
  1450  							},
  1451  						},
  1452  					},
  1453  				},
  1454  			},
  1455  			wantErr: proto.ErrAdvisoryMissing,
  1456  		},
  1457  		{
  1458  			desc: "Failed_scan",
  1459  			res: &scalibr.ScanResult{
  1460  				Version:   "1.0.0",
  1461  				StartTime: startTime,
  1462  				EndTime:   endTime,
  1463  				Status:    failure,
  1464  				PluginStatus: []*plugin.Status{
  1465  					{
  1466  						Name:    "ext",
  1467  						Version: 2,
  1468  						Status:  failure,
  1469  					},
  1470  					{
  1471  						Name:    "det",
  1472  						Version: 3,
  1473  						Status:  failure,
  1474  					},
  1475  				},
  1476  			},
  1477  			want: &spb.ScanResult{
  1478  				Version:   "1.0.0",
  1479  				StartTime: timestamppb.New(startTime),
  1480  				EndTime:   timestamppb.New(endTime),
  1481  				Status:    failureProto,
  1482  				Inventory: &spb.Inventory{},
  1483  				PluginStatus: []*spb.PluginStatus{
  1484  					{
  1485  						Name:    "ext",
  1486  						Version: 2,
  1487  						Status:  failureProto,
  1488  					},
  1489  					{
  1490  						Name:    "det",
  1491  						Version: 3,
  1492  						Status:  failureProto,
  1493  					},
  1494  				},
  1495  			},
  1496  		},
  1497  		{
  1498  			desc: "pom.xml_inventories_with_transitive_dependencies",
  1499  			res: &scalibr.ScanResult{
  1500  				Version:   "1.0.0",
  1501  				StartTime: startTime,
  1502  				EndTime:   endTime,
  1503  				Status:    success,
  1504  				Inventory: inventory.Inventory{
  1505  					Packages: []*extractor.Package{mavenPackage},
  1506  				},
  1507  			},
  1508  			want: &spb.ScanResult{
  1509  				Version:   "1.0.0",
  1510  				StartTime: timestamppb.New(startTime),
  1511  				EndTime:   timestamppb.New(endTime),
  1512  				Status:    successProto,
  1513  				Inventory: &spb.Inventory{
  1514  					Packages:        []*spb.Package{mavenPackageProto},
  1515  					GenericFindings: []*spb.GenericFinding{},
  1516  				},
  1517  			},
  1518  		},
  1519  		{
  1520  			desc: "secret_containing_a_GCP_service_account_key",
  1521  			res: &scalibr.ScanResult{
  1522  				Version:   "1.0.0",
  1523  				StartTime: startTime,
  1524  				EndTime:   endTime,
  1525  				Status:    success,
  1526  				Inventory: inventory.Inventory{
  1527  					Secrets: []*inventory.Secret{gcpsakSecret},
  1528  				},
  1529  			},
  1530  			want: &spb.ScanResult{
  1531  				Version:   "1.0.0",
  1532  				StartTime: timestamppb.New(startTime),
  1533  				EndTime:   timestamppb.New(endTime),
  1534  				Status:    successProto,
  1535  				Inventory: &spb.Inventory{
  1536  					Secrets: []*spb.Secret{gcpsakSecretProto},
  1537  				},
  1538  			},
  1539  		},
  1540  		{
  1541  			desc: "secret_containing_a_GCP_service_account_key_with_extra_information",
  1542  			res: &scalibr.ScanResult{
  1543  				Version:   "1.0.0",
  1544  				StartTime: startTime,
  1545  				EndTime:   endTime,
  1546  				Status:    success,
  1547  				Inventory: inventory.Inventory{
  1548  					Secrets: []*inventory.Secret{gcpsakSecretWithExtra},
  1549  				},
  1550  			},
  1551  			want: &spb.ScanResult{
  1552  				Version:   "1.0.0",
  1553  				StartTime: timestamppb.New(startTime),
  1554  				EndTime:   timestamppb.New(endTime),
  1555  				Status:    successProto,
  1556  				Inventory: &spb.Inventory{
  1557  					Secrets: []*spb.Secret{gcpsakSecretProtoWithExtra},
  1558  				},
  1559  			},
  1560  		},
  1561  		{
  1562  			desc: "package_containing_license_data",
  1563  			res: &scalibr.ScanResult{
  1564  				Version:   "1.0.0",
  1565  				StartTime: startTime,
  1566  				EndTime:   endTime,
  1567  				Status:    success,
  1568  				Inventory: inventory.Inventory{
  1569  					Packages: []*extractor.Package{licensePackage},
  1570  				},
  1571  			},
  1572  			want: &spb.ScanResult{
  1573  				Version:   "1.0.0",
  1574  				StartTime: timestamppb.New(startTime),
  1575  				EndTime:   timestamppb.New(endTime),
  1576  				Status:    successProto,
  1577  				Inventory: &spb.Inventory{
  1578  					Packages: []*spb.Package{licensePackageProto},
  1579  				},
  1580  			},
  1581  		},
  1582  	}
  1583  
  1584  	for _, tc := range testCases {
  1585  		t.Run(tc.desc, func(t *testing.T) {
  1586  			if slices.Contains(tc.excludeForOS, runtime.GOOS) {
  1587  				t.Skipf("Skipping test %q on %s", tc.desc, runtime.GOOS)
  1588  			}
  1589  
  1590  			got, err := proto.ScanResultToProto(tc.res)
  1591  			if !errors.Is(err, tc.wantErr) {
  1592  				t.Fatalf("proto.ScanResultToProto(%v) err: got %v, want %v", tc.res, err, tc.wantErr)
  1593  			}
  1594  
  1595  			opts := append([]cmp.Option{
  1596  				protocmp.Transform(),
  1597  				cmpopts.EquateEmpty(),
  1598  			}, pkgOpts...)
  1599  
  1600  			if diff := cmp.Diff(tc.want, got, opts...); diff != "" {
  1601  				t.Errorf("proto.ScanResultToProto(%v) returned unexpected diff (-want +got):\n%s", tc.res, diff)
  1602  			}
  1603  
  1604  			if err != nil {
  1605  				return
  1606  			}
  1607  
  1608  			gotStruct, err := proto.ScanResultFromProto(tc.want)
  1609  			if !errors.Is(err, tc.wantErr) {
  1610  				t.Fatalf("proto.ScanResultToProto(%v) err: got %v, want %v", tc.res, err, tc.wantErr)
  1611  			}
  1612  			if diff := cmp.Diff(tc.res, gotStruct, opts...); diff != "" {
  1613  				t.Errorf("proto.ScanResultToProto(%v) returned unexpected diff (-want +got):\n%s", tc.res, diff)
  1614  			}
  1615  
  1616  			// TODO - b/421456154: test conversion of remaining types.
  1617  			invProto := protobuf.Clone(got.GetInventory()).(*spb.Inventory)
  1618  			invProto.GenericFindings = nil
  1619  			invProto.Secrets = nil
  1620  
  1621  			tc.res.Inventory.GenericFindings = nil
  1622  			tc.res.Inventory.Secrets = nil
  1623  
  1624  			opts = []cmp.Option{
  1625  				cmpopts.IgnoreFields(extractor.LayerMetadata{}, "ParentContainer"),
  1626  				cmpopts.EquateEmpty(),
  1627  			}
  1628  
  1629  			gotInv := proto.InventoryToStruct(invProto)
  1630  			if diff := cmp.Diff(tc.res.Inventory, *gotInv, opts...); diff != "" {
  1631  				t.Errorf("proto.InventoryToStruct(%v) returned unexpected diff (-want +got):\n%s", invProto, diff)
  1632  			}
  1633  		})
  1634  	}
  1635  }