github.com/google/osv-scalibr@v0.4.1/annotator/osduplicate/apk/apk_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 apk_test
    16  
    17  import (
    18  	"context"
    19  	"os"
    20  	"path/filepath"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"github.com/google/go-cmp/cmp/cmpopts"
    25  	"github.com/google/go-cpy/cpy"
    26  	"github.com/google/osv-scalibr/annotator"
    27  	"github.com/google/osv-scalibr/annotator/osduplicate/apk"
    28  	"github.com/google/osv-scalibr/extractor"
    29  	scalibrfs "github.com/google/osv-scalibr/fs"
    30  	"github.com/google/osv-scalibr/inventory"
    31  	"github.com/google/osv-scalibr/inventory/vex"
    32  	"google.golang.org/protobuf/proto"
    33  )
    34  
    35  func TestAnnotate(t *testing.T) {
    36  	cancelledContext, cancel := context.WithCancel(context.Background())
    37  	cancel()
    38  
    39  	copier := cpy.New(
    40  		cpy.Func(proto.Clone),
    41  		cpy.IgnoreAllUnexported(),
    42  	)
    43  
    44  	tests := []struct {
    45  		desc     string
    46  		apkDB    string
    47  		packages []*extractor.Package
    48  		//nolint:containedctx
    49  		ctx          context.Context
    50  		wantErr      error
    51  		wantPackages []*extractor.Package
    52  	}{
    53  		{
    54  			desc:  "empty_db",
    55  			apkDB: "testdata/empty",
    56  			packages: []*extractor.Package{
    57  				{
    58  					Name:      "libstdc++",
    59  					Locations: []string{"usr/lib/libstdc++.so.6.0.33"},
    60  				},
    61  			},
    62  			wantPackages: []*extractor.Package{
    63  				{
    64  					Name:      "libstdc++",
    65  					Locations: []string{"usr/lib/libstdc++.so.6.0.33"},
    66  				},
    67  			},
    68  		},
    69  		{
    70  			desc:  "some_pkgs_found_in_db",
    71  			apkDB: "testdata/some",
    72  			packages: []*extractor.Package{
    73  				{
    74  					Name:      "libstdc++",
    75  					Locations: []string{"usr/lib/libstdc++.so.6.0.33"},
    76  				},
    77  				{
    78  					Name:      "not-in-db",
    79  					Locations: []string{"path/not/in/db"},
    80  				},
    81  			},
    82  			wantPackages: []*extractor.Package{
    83  				{
    84  					Name:      "libstdc++",
    85  					Locations: []string{"usr/lib/libstdc++.so.6.0.33"},
    86  					ExploitabilitySignals: []*vex.PackageExploitabilitySignal{&vex.PackageExploitabilitySignal{
    87  						Plugin:          apk.Name,
    88  						Justification:   vex.ComponentNotPresent,
    89  						MatchesAllVulns: true,
    90  					}},
    91  				},
    92  				{
    93  					Name:      "not-in-db",
    94  					Locations: []string{"path/not/in/db"},
    95  				},
    96  			},
    97  		},
    98  		{
    99  			desc:  "ctx_cancelled",
   100  			ctx:   cancelledContext,
   101  			apkDB: "testdata/some",
   102  			packages: []*extractor.Package{
   103  				{
   104  					Name:      "libstdc++",
   105  					Locations: []string{"usr/lib/libstdc++.so.6.0.33"},
   106  				},
   107  			},
   108  			wantPackages: []*extractor.Package{
   109  				{
   110  					Name:      "libstdc++",
   111  					Locations: []string{"usr/lib/libstdc++.so.6.0.33"},
   112  					// No annotations
   113  				},
   114  			},
   115  			wantErr: cmpopts.AnyError,
   116  		},
   117  	}
   118  
   119  	for _, tt := range tests {
   120  		t.Run(tt.desc, func(t *testing.T) {
   121  			if tt.ctx == nil {
   122  				tt.ctx = context.Background()
   123  			}
   124  
   125  			tmpPath := setupApkDB(t, tt.apkDB)
   126  			input := &annotator.ScanInput{
   127  				ScanRoot: scalibrfs.RealFSScanRoot(tmpPath),
   128  			}
   129  
   130  			// Deep copy the packages to avoid modifying the original inventory that is used in other tests.
   131  			packages := copier.Copy(tt.packages).([]*extractor.Package)
   132  			inv := &inventory.Inventory{Packages: packages}
   133  
   134  			err := apk.New().Annotate(tt.ctx, input, inv)
   135  			if !cmp.Equal(tt.wantErr, err, cmpopts.EquateErrors()) {
   136  				t.Fatalf("Annotate(%v) error: %v, want %v", tt.packages, err, tt.wantErr)
   137  			}
   138  
   139  			want := &inventory.Inventory{Packages: tt.wantPackages}
   140  			if diff := cmp.Diff(want, inv); diff != "" {
   141  				t.Errorf("Annotate(%v): unexpected diff (-want +got): %v", tt.packages, diff)
   142  			}
   143  		})
   144  	}
   145  }
   146  
   147  // Sets up the apk db
   148  func setupApkDB(t *testing.T, file string) string {
   149  	t.Helper()
   150  	dir := t.TempDir()
   151  	dbFolder := filepath.Join(dir, "lib/apk/db/")
   152  	if err := os.MkdirAll(dbFolder, 0777); err != nil {
   153  		t.Fatalf("error creating directory %q: %v", dbFolder, err)
   154  	}
   155  
   156  	content, err := os.ReadFile(file)
   157  	if err != nil {
   158  		t.Fatalf("Error reading content file %q: %v", content, err)
   159  	}
   160  
   161  	dbFile := filepath.Join(dbFolder, "installed")
   162  	if err := os.WriteFile(dbFile, content, 0644); err != nil {
   163  		t.Fatalf("Error creating file %q: %v", dbFile, err)
   164  	}
   165  
   166  	return dir
   167  }