github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/embeddedfs/archive/archive_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 archive_test
    16  
    17  import (
    18  	"fmt"
    19  	"io"
    20  	"strings"
    21  	"testing"
    22  
    23  	"github.com/google/go-cmp/cmp"
    24  	"github.com/google/go-cmp/cmp/cmpopts"
    25  	cpb "github.com/google/osv-scalibr/binary/proto/config_go_proto"
    26  	"github.com/google/osv-scalibr/extractor/filesystem/embeddedfs/archive"
    27  	"github.com/google/osv-scalibr/extractor/filesystem/simplefileapi"
    28  	"github.com/google/osv-scalibr/testing/extracttest"
    29  	"github.com/google/osv-scalibr/testing/fakefs"
    30  )
    31  
    32  func TestFileRequired(t *testing.T) {
    33  	tests := []struct {
    34  		desc                  string
    35  		path                  string
    36  		fileSize              int64
    37  		maxFileSize           int64
    38  		pluginSpecificMaxSize int64
    39  		want                  bool
    40  	}{
    41  		{
    42  			desc: "tar.gz",
    43  			path: "archive.tar.gz",
    44  			want: true,
    45  		},
    46  		{
    47  			desc: "tar",
    48  			path: "archive.tar",
    49  			want: true,
    50  		},
    51  		{
    52  			desc: "unsupported_extension",
    53  			path: "document.txt",
    54  			want: false,
    55  		},
    56  		{
    57  			desc: "no_extension",
    58  			path: "noextension",
    59  			want: false,
    60  		},
    61  		{
    62  			desc:        "file_size_below_limit",
    63  			path:        "archive.tar.gz",
    64  			fileSize:    1000,
    65  			maxFileSize: 1000,
    66  			want:        true,
    67  		},
    68  		{
    69  			desc:        "file_size_above_limit",
    70  			path:        "archive.tar.gz",
    71  			fileSize:    1001,
    72  			maxFileSize: 1000,
    73  			want:        false,
    74  		},
    75  		{
    76  			desc:                  "override_global_size_below_limit",
    77  			path:                  "archive.tar.gz",
    78  			fileSize:              1001,
    79  			maxFileSize:           1000,
    80  			pluginSpecificMaxSize: 1001,
    81  			want:                  true,
    82  		},
    83  		{
    84  			desc:                  "override_global_size_above_limit",
    85  			path:                  "archive.tar.gz",
    86  			fileSize:              1001,
    87  			maxFileSize:           1001,
    88  			pluginSpecificMaxSize: 1000,
    89  			want:                  false,
    90  		},
    91  	}
    92  
    93  	for _, tt := range tests {
    94  		t.Run(tt.desc, func(t *testing.T) {
    95  			e := archive.New(&cpb.PluginConfig{
    96  				MaxFileSizeBytes: tt.maxFileSize,
    97  				PluginSpecific: []*cpb.PluginSpecificConfig{
    98  					{Config: &cpb.PluginSpecificConfig_Archive{Archive: &cpb.ArchiveConfig{MaxFileSizeBytes: tt.pluginSpecificMaxSize}}},
    99  				},
   100  			})
   101  			if got := e.FileRequired(simplefileapi.New(tt.path, fakefs.FakeFileInfo{
   102  				FileSize: tt.fileSize,
   103  			})); got != tt.want {
   104  				t.Errorf("FileRequired(%q) = %v, want %v", tt.path, got, tt.want)
   105  			}
   106  		})
   107  	}
   108  }
   109  
   110  func TestExtract(t *testing.T) {
   111  	tests := []struct {
   112  		name        string
   113  		inputConfig extracttest.ScanInputMockConfig
   114  		wantFiles   map[string]string
   115  		wantErr     error
   116  	}{
   117  		{
   118  			name: "regular_tar",
   119  			inputConfig: extracttest.ScanInputMockConfig{
   120  				Path: "testdata/archive.tar",
   121  			},
   122  			wantFiles: map[string]string{"file.txt": "tar contents"},
   123  		},
   124  		{
   125  			name: "gzipped_tar",
   126  			inputConfig: extracttest.ScanInputMockConfig{
   127  				Path: "testdata/archive.tar.gz",
   128  			},
   129  			wantFiles: map[string]string{"file.txt": "tar.gz contents"},
   130  		},
   131  		{
   132  			name: "not_an_archive",
   133  			inputConfig: extracttest.ScanInputMockConfig{
   134  				Path: "testdata/not-an-archive.txt",
   135  			},
   136  			wantErr: cmpopts.AnyError,
   137  		},
   138  	}
   139  
   140  	for _, tt := range tests {
   141  		t.Run(tt.name, func(t *testing.T) {
   142  			e := archive.New(&cpb.PluginConfig{})
   143  			scanInput := extracttest.GenerateScanInputMock(t, tt.inputConfig)
   144  			defer extracttest.CloseTestScanInput(t, scanInput)
   145  
   146  			inv, err := e.Extract(t.Context(), &scanInput)
   147  			if !cmp.Equal(err, tt.wantErr, cmpopts.EquateErrors()) {
   148  				t.Fatalf("Extract(%+v) error: got %v, want %v\n", tt.name, err, tt.wantErr)
   149  			}
   150  
   151  			if tt.wantErr == nil && len(inv.EmbeddedFSs) == 0 {
   152  				t.Fatal("No embedded FS returned")
   153  			}
   154  
   155  			for i, embeddedFS := range inv.EmbeddedFSs {
   156  				t.Run(fmt.Sprintf("archive_%d", i), func(t *testing.T) {
   157  					fs, err := embeddedFS.GetEmbeddedFS(t.Context())
   158  					if err != nil {
   159  						t.Errorf("GetEmbeddedFS(): %v", err)
   160  					}
   161  
   162  					if _, err := fs.ReadDir("/"); err != nil {
   163  						t.Fatalf("fs.ReadDir(/): %v", err)
   164  					}
   165  					if _, err := fs.Stat("/"); err != nil {
   166  						t.Fatalf("fs.Stat(/): %v", err)
   167  					}
   168  
   169  					for wantPath, wantContent := range tt.wantFiles {
   170  						f, err := fs.Open(wantPath)
   171  						if err != nil {
   172  							t.Fatalf("fs.Open(%q): %v", wantPath, err)
   173  						}
   174  						defer f.Close()
   175  
   176  						bytes, err := io.ReadAll(f)
   177  						if err != nil {
   178  							t.Fatalf("ReadAll: %v", err)
   179  						}
   180  
   181  						if !strings.HasPrefix(string(bytes), wantContent) {
   182  							t.Fatalf("got %q content: %q, want %q", wantPath, string(bytes), wantContent)
   183  						}
   184  					}
   185  				})
   186  			}
   187  		})
   188  	}
   189  }