github.com/google/osv-scalibr@v0.4.1/detector/govulncheck/binary/binary_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 binary_test
    16  
    17  import (
    18  	"os"
    19  	"path/filepath"
    20  	"runtime"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	cpb "github.com/google/osv-scalibr/binary/proto/config_go_proto"
    25  	"github.com/google/osv-scalibr/detector/govulncheck/binary"
    26  	"github.com/google/osv-scalibr/extractor"
    27  	"github.com/google/osv-scalibr/extractor/filesystem/language/golang/gobinary"
    28  	scalibrfs "github.com/google/osv-scalibr/fs"
    29  	"github.com/google/osv-scalibr/inventory"
    30  	"github.com/google/osv-scalibr/packageindex"
    31  	"github.com/google/osv-scalibr/purl"
    32  	osvpb "github.com/ossf/osv-schema/bindings/go/osvschema"
    33  	"google.golang.org/protobuf/testing/protocmp"
    34  	"google.golang.org/protobuf/types/known/timestamppb"
    35  )
    36  
    37  const binaryName = "semaphore-demo-go"
    38  
    39  func TestScan(t *testing.T) {
    40  	wd, err := os.Getwd()
    41  	if err != nil {
    42  		t.Fatalf("os.Getwd(): %v", err)
    43  	}
    44  	// Govulncheck expects the path to be file:///c:/something
    45  	if runtime.GOOS == "windows" {
    46  		wd = "/" + wd
    47  	}
    48  	det := binary.New(&cpb.PluginConfig{
    49  		PluginSpecific: []*cpb.PluginSpecificConfig{
    50  			{Config: &cpb.PluginSpecificConfig_Govulncheck{Govulncheck: &cpb.GovulncheckConfig{
    51  				OfflineVulnDbPath: filepath.ToSlash(filepath.Join(wd, "testdata", "vulndb")),
    52  			}}},
    53  		},
    54  	})
    55  	px := setupPackageIndex([]string{binaryName})
    56  	findings, err := det.Scan(t.Context(), scalibrfs.RealFSScanRoot("."), px)
    57  	if err != nil {
    58  		t.Fatalf("detector.Scan(%v): %v", px, err)
    59  	}
    60  	// There are two vulns in the test vulndb defined for two
    61  	// module dependencies of the test binary. Both dependencies
    62  	// are used at a vulnerable version. However, for only one
    63  	// there is a vulnerable symbol present in the binary.
    64  	if len(findings.PackageVulns) != 1 {
    65  		t.Fatalf("detector.Scan(%v): expected 1 finding, got: %v", px, findings.PackageVulns)
    66  	}
    67  	got := findings.PackageVulns[0]
    68  	want := &inventory.PackageVuln{
    69  		Vulnerability: &osvpb.Vulnerability{
    70  			Id:        "GO-2022-1144",
    71  			Modified:  &timestamppb.Timestamp{},
    72  			Published: &timestamppb.Timestamp{},
    73  			Withdrawn: &timestamppb.Timestamp{},
    74  			Aliases:   []string{"CVE-2022-41717", "GHSA-xrjj-mj9h-534m"},
    75  			Summary:   "Excessive memory growth in net/http and golang.org/x/net/http2",
    76  			Details: "An attacker can cause excessive memory growth in a Go server accepting HTTP/2 requests.\n\n" +
    77  				"HTTP/2 server connections contain a cache of HTTP header keys sent by the client. While the total " +
    78  				"number of entries in this cache is capped, an attacker sending very large keys can cause the " +
    79  				"server to allocate approximately 64 MiB per open connection.",
    80  			Affected: []*osvpb.Affected{
    81  				{
    82  					Package: &osvpb.Package{Ecosystem: "Go", Name: "stdlib"},
    83  				},
    84  			},
    85  			References: []*osvpb.Reference{
    86  				{Type: osvpb.Reference_REPORT, Url: "https://go.dev/issue/56350"},
    87  				{Type: osvpb.Reference_FIX, Url: "https://go.dev/cl/455717"},
    88  				{Type: osvpb.Reference_FIX, Url: "https://go.dev/cl/455635"},
    89  				{
    90  					Type: osvpb.Reference_WEB,
    91  					Url:  "https://groups.google.com/g/golang-announce/c/L_3rmdT0BMU/m/yZDrXjIiBQAJ",
    92  				},
    93  			},
    94  			Credits: []*osvpb.Credit{{Name: "Josselin Costanzi"}},
    95  		},
    96  	}
    97  
    98  	// Remove some fields that might change between govulncheck versions.
    99  	got.Vulnerability.SchemaVersion = ""
   100  	got.Vulnerability.Modified = &timestamppb.Timestamp{}
   101  	got.Vulnerability.Published = &timestamppb.Timestamp{}
   102  	got.Vulnerability.Withdrawn = &timestamppb.Timestamp{}
   103  	got.Vulnerability.Affected = []*osvpb.Affected{got.Vulnerability.Affected[0]}
   104  	got.Vulnerability.Affected[0].Ranges = nil
   105  	got.Vulnerability.Affected[0].EcosystemSpecific = nil
   106  	got.Vulnerability.DatabaseSpecific = nil
   107  
   108  	if diff := cmp.Diff(want, got, protocmp.Transform()); diff != "" {
   109  		t.Errorf("detector.Scan(%v): unexpected findings (-want +got):\n%s", px, diff)
   110  	}
   111  }
   112  
   113  func TestScanErrorInGovulncheck(t *testing.T) {
   114  	wd, err := os.Getwd()
   115  	if err != nil {
   116  		t.Fatalf("os.Getwd(): %v", err)
   117  	}
   118  	// Govulncheck expects the path to be file:///c:/something
   119  	if runtime.GOOS == "windows" {
   120  		wd = "/" + wd
   121  	}
   122  	det := binary.New(&cpb.PluginConfig{
   123  		PluginSpecific: []*cpb.PluginSpecificConfig{
   124  			{Config: &cpb.PluginSpecificConfig_Govulncheck{Govulncheck: &cpb.GovulncheckConfig{
   125  				OfflineVulnDbPath: filepath.ToSlash(filepath.Join(wd, "testdata", "vulndb")),
   126  			}}},
   127  		},
   128  	})
   129  	px := setupPackageIndex([]string{"nonexistent", binaryName})
   130  	result, err := det.Scan(t.Context(), scalibrfs.RealFSScanRoot("."), px)
   131  	if err == nil {
   132  		t.Fatalf("detector.Scan(%v): Expected an error, got none", px)
   133  	}
   134  	if len(result.PackageVulns) == 0 {
   135  		t.Fatalf("detector.Scan(%v): Expected scan results, got none", px)
   136  	}
   137  }
   138  
   139  func setupPackageIndex(names []string) *packageindex.PackageIndex {
   140  	var pkgs []*extractor.Package
   141  	for _, n := range names {
   142  		pkgs = append(pkgs, &extractor.Package{
   143  			Name:      n,
   144  			Version:   "1.2.3",
   145  			PURLType:  purl.TypeGolang,
   146  			Locations: []string{filepath.Join("testdata", n)},
   147  			Plugins:   []string{gobinary.Name},
   148  		})
   149  	}
   150  	px, _ := packageindex.New(pkgs)
   151  	return px
   152  }