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