github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/embeddedfs/vdi/vdi_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 vdi_test 16 17 import ( 18 "errors" 19 "fmt" 20 "io" 21 "os" 22 "path/filepath" 23 "strings" 24 "testing" 25 26 cpb "github.com/google/osv-scalibr/binary/proto/config_go_proto" 27 "github.com/google/osv-scalibr/extractor/filesystem" 28 "github.com/google/osv-scalibr/extractor/filesystem/embeddedfs/vdi" 29 "github.com/google/osv-scalibr/extractor/filesystem/simplefileapi" 30 "github.com/google/osv-scalibr/testing/fakefs" 31 ) 32 33 func TestFileRequired(t *testing.T) { 34 tests := []struct { 35 desc string 36 path string 37 fileSize int64 38 maxFileSize int64 39 pluginSpecificMaxSize int64 40 want bool 41 }{ 42 { 43 desc: "vdi_lowercase", 44 path: "testdata/disk.vdi", 45 want: true, 46 }, 47 { 48 desc: "vdi_uppercase", 49 path: "testdata/DISK.VDI", 50 want: true, 51 }, 52 { 53 desc: "not_vdi", 54 path: "testdata/document.txt", 55 want: false, 56 }, 57 { 58 desc: "no_extension", 59 path: "testdata/noextension", 60 want: false, 61 }, 62 { 63 desc: "override_global_size_below_limit", 64 path: "disk.vdi", 65 fileSize: 1001, 66 maxFileSize: 1000, 67 pluginSpecificMaxSize: 1001, 68 want: true, 69 }, 70 { 71 desc: "override_global_size_above_limit", 72 path: "disk.vdi", 73 fileSize: 1001, 74 maxFileSize: 1001, 75 pluginSpecificMaxSize: 1000, 76 want: false, 77 }, 78 } 79 80 for _, tt := range tests { 81 t.Run(tt.desc, func(t *testing.T) { 82 extractor := vdi.New(&cpb.PluginConfig{ 83 MaxFileSizeBytes: tt.maxFileSize, 84 PluginSpecific: []*cpb.PluginSpecificConfig{ 85 {Config: &cpb.PluginSpecificConfig_Vdi{Vdi: &cpb.VDIConfig{MaxFileSizeBytes: tt.pluginSpecificMaxSize}}}, 86 }, 87 }) 88 if got := extractor.FileRequired(simplefileapi.New(tt.path, fakefs.FakeFileInfo{ 89 FileSize: tt.fileSize, 90 })); got != tt.want { 91 t.Errorf("FileRequired(%q) = %v, want %v", tt.path, got, tt.want) 92 } 93 }) 94 } 95 } 96 97 func TestExtractValidVDI(t *testing.T) { 98 extractor := vdi.New(&cpb.PluginConfig{}) 99 100 tests := []struct { 101 name string 102 path string 103 }{ 104 { 105 name: "StaticVDI", 106 path: filepath.FromSlash("testdata/valid-ext-exfat-fat32-ntfs-static.vdi"), 107 }, 108 { 109 name: "DynamicVDI", 110 path: filepath.FromSlash("testdata/valid-ext-exfat-fat32-ntfs-dynamic.vdi"), 111 }, 112 } 113 114 for _, tt := range tests { 115 t.Run(tt.name, func(t *testing.T) { 116 info, err := os.Stat(tt.path) 117 if err != nil { 118 t.Fatalf("os.Stat(%q) failed: %v", tt.path, err) 119 } 120 121 f, err := os.Open(tt.path) 122 if err != nil { 123 t.Fatalf("os.Open(%q) failed: %v", tt.path, err) 124 } 125 defer f.Close() 126 127 input := &filesystem.ScanInput{ 128 Path: tt.path, 129 Root: ".", 130 Info: info, 131 Reader: f, 132 FS: nil, 133 } 134 135 ctx := t.Context() 136 inv, err := extractor.Extract(ctx, input) 137 if err != nil { 138 t.Fatalf("Extract(%q) failed: %v", tt.path, err) 139 } 140 141 if len(inv.EmbeddedFSs) == 0 { 142 t.Fatal("Extract returned no DiskImages") 143 } 144 145 for i, embeddedFS := range inv.EmbeddedFSs { 146 t.Run(fmt.Sprintf("DiskImage_%d", i), func(t *testing.T) { 147 if !strings.HasPrefix(embeddedFS.Path, tt.path) { 148 t.Errorf("EmbeddedFS.Path = %q, want prefix %q", embeddedFS.Path, tt.path) 149 } 150 151 fs, err := embeddedFS.GetEmbeddedFS(ctx) 152 if err != nil { 153 t.Errorf("GetEmbeddedFS() failed: %v", err) 154 } 155 156 entries, err := fs.ReadDir("/") 157 if err != nil { 158 t.Fatalf("fs.ReadDir(/) failed: %v", err) 159 } 160 t.Logf("ReadDir(/) returned %d entries", len(entries)) 161 162 info, err := fs.Stat("/") 163 if err != nil { 164 t.Fatalf("fs.Stat(/) failed: %v", err) 165 } 166 if !info.IsDir() { 167 t.Errorf("fs.Stat(/) IsDir() = %v, want true", info.IsDir()) 168 } 169 170 found := false 171 for _, entry := range entries { 172 name := entry.Name() 173 if strings.HasSuffix(name, ".pem") { 174 found = true 175 filePath := name 176 f, err := fs.Open(filePath) 177 if err != nil { 178 t.Fatalf("fs.Open(%q) failed: %v", filePath, err) 179 } 180 defer f.Close() 181 182 buf := make([]byte, 4096) 183 n, err := f.Read(buf) 184 if err != nil && !errors.Is(err, io.EOF) { 185 t.Errorf("f.Read(%q) failed: %v", filePath, err) 186 } 187 t.Logf("Read %d bytes from %s\n", n, name) 188 189 // The buffer must start with "-----BEGIN" 190 if string(buf[:10]) != "-----BEGIN" { 191 t.Errorf("%s contains unexpected data!", filePath) 192 } 193 194 info, err := f.Stat() 195 if err != nil { 196 t.Errorf("f.Stat(%q) failed: %v", filePath, err) 197 } else if info.IsDir() { 198 t.Errorf("f.Stat(%q) IsDir() = %v, want false", filePath, info.IsDir()) 199 } 200 break 201 } 202 } 203 if !found { 204 t.Errorf("private keys not found") 205 } 206 }) 207 } 208 }) 209 } 210 } 211 212 func TestExtractInvalidVDI(t *testing.T) { 213 extractor := vdi.New(&cpb.PluginConfig{}) 214 path := filepath.FromSlash("testdata/invalid.vdi") 215 info, err := os.Stat(path) 216 if err != nil { 217 t.Fatalf("os.Stat(%q) failed: %v", path, err) 218 } 219 220 f, err := os.Open(path) 221 if err != nil { 222 t.Fatalf("os.Open(%q) failed: %v", path, err) 223 } 224 defer f.Close() 225 226 input := &filesystem.ScanInput{ 227 Path: path, 228 Root: ".", 229 Info: info, 230 Reader: f, 231 FS: nil, 232 } 233 234 ctx := t.Context() 235 _, err = extractor.Extract(ctx, input) 236 if err == nil { 237 t.Errorf("Extract(%q) succeeded, want error", path) 238 } 239 } 240 241 func TestExtractNonExistentVDI(t *testing.T) { 242 extractor := vdi.New(&cpb.PluginConfig{}) 243 path := filepath.FromSlash("testdata/nonexistent.vdi") 244 input := &filesystem.ScanInput{ 245 Path: path, 246 Root: "testdata", 247 Info: nil, 248 Reader: nil, 249 FS: nil, 250 } 251 252 ctx := t.Context() 253 _, err := extractor.Extract(ctx, input) 254 if err == nil { 255 t.Errorf("Extract(%q) succeeded, want error", path) 256 } 257 }