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 }