github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/containers/podman/podman_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 //go:build linux 16 17 package podman_test 18 19 import ( 20 "os" 21 "path/filepath" 22 "testing" 23 "time" 24 25 "github.com/google/go-cmp/cmp" 26 "github.com/google/go-cmp/cmp/cmpopts" 27 "github.com/google/osv-scalibr/extractor" 28 "github.com/google/osv-scalibr/extractor/filesystem" 29 "github.com/google/osv-scalibr/extractor/filesystem/containers/podman" 30 "github.com/google/osv-scalibr/extractor/filesystem/simplefileapi" 31 scalibrfs "github.com/google/osv-scalibr/fs" 32 "github.com/google/osv-scalibr/testing/extracttest" 33 ) 34 35 func TestExtractor_FileRequired(t *testing.T) { 36 tests := []struct { 37 inputPath string 38 want bool 39 }{ 40 { 41 inputPath: "", want: false, 42 }, 43 { 44 inputPath: "/home/user/.local/share/containers/storage/db.sql", want: true, 45 }, 46 { 47 inputPath: "/home/user/.local/share/containers/storage/libpod/bolt_state.db", want: true, 48 }, 49 { 50 inputPath: "/home/user/.local/something.db", want: false, 51 }, 52 { 53 inputPath: "/home/user/.local/db.sql", want: false, 54 }, 55 } 56 for _, tt := range tests { 57 t.Run(tt.inputPath, func(t *testing.T) { 58 e := podman.Extractor{} 59 got := e.FileRequired(simplefileapi.New(tt.inputPath, nil)) 60 if got != tt.want { 61 t.Errorf("FileRequired(%s) got = %v, want %v", tt.inputPath, got, tt.want) 62 } 63 }) 64 } 65 } 66 67 func TestExtractor_Extract(t *testing.T) { 68 // extracttest.TestTableEntry + podman config 69 type testTableEntry struct { 70 Name string 71 Path string 72 WantPackages []*extractor.Package 73 WantErr error 74 Config podman.Config 75 } 76 77 tests := []testTableEntry{ 78 { 79 // The SQLite driver doesn't fail when opening an improperly formatted file, 80 // so the error appears during the container listing phase. 81 Name: "invalid_sqlite_db", 82 Path: "testdata/notdb.sql", 83 WantErr: extracttest.ContainsErrStr{Str: "error listing containers in file"}, 84 }, 85 { 86 Name: "invalid boltstatedb", 87 Path: "testdata/not_bolt_state.db", 88 WantErr: extracttest.ContainsErrStr{Str: "error opening file"}, 89 }, 90 { 91 Name: "valid using sqlite3 - all", 92 Path: "testdata/db.sql", 93 Config: podman.Config{IncludeStopped: true}, 94 WantPackages: []*extractor.Package{ 95 { 96 Name: "docker.io/hello-world", 97 Version: "f1f77a0f96b7251d7ef5472705624e2d76db64855b5b121e1cbefe9dc52d0f86", 98 Metadata: &podman.Metadata{ 99 Status: "exited", 100 Exited: true, 101 }, 102 Locations: []string{"db.sql"}, 103 }, 104 { 105 Name: "postgres", 106 Version: "e92968df83750a723114bf998e3e323dda53e4c5c3ea42b22dd6ad6e3df80ca5", 107 Metadata: &podman.Metadata{ 108 ExposedPorts: map[uint16][]string{5432: {"tcp"}}, 109 PID: 37461, 110 Status: "running", 111 }, 112 Locations: []string{"db.sql"}, 113 }, 114 { 115 Name: "redis", 116 Version: "a8036f14f15ead9517115576fb4462894a000620c2be556410f6c24afb8a482b", 117 Metadata: &podman.Metadata{ 118 ExposedPorts: map[uint16][]string{6379: {"tcp"}}, 119 PID: 37379, 120 Status: "running", 121 }, 122 Locations: []string{"db.sql"}, 123 }, 124 }, 125 }, 126 { 127 Name: "valid using sqlite3 - running", 128 Path: "testdata/db.sql", 129 WantPackages: []*extractor.Package{ 130 { 131 Name: "postgres", 132 Version: "e92968df83750a723114bf998e3e323dda53e4c5c3ea42b22dd6ad6e3df80ca5", 133 Metadata: &podman.Metadata{ 134 ExposedPorts: map[uint16][]string{5432: {"tcp"}}, 135 PID: 37461, 136 Status: "running", 137 }, 138 Locations: []string{"db.sql"}, 139 }, 140 { 141 Name: "redis", 142 Version: "a8036f14f15ead9517115576fb4462894a000620c2be556410f6c24afb8a482b", 143 Metadata: &podman.Metadata{ 144 ExposedPorts: map[uint16][]string{6379: {"tcp"}}, 145 PID: 37379, 146 Status: "running", 147 }, 148 Locations: []string{"db.sql"}, 149 }, 150 }, 151 }, 152 { 153 Name: "valid using bolt - all", 154 Path: "testdata/bolt_state.db", 155 Config: podman.Config{IncludeStopped: true}, 156 WantPackages: []*extractor.Package{ 157 { 158 Name: "docker.io/hello-world", 159 Version: "f1f77a0f96b7251d7ef5472705624e2d76db64855b5b121e1cbefe9dc52d0f86", 160 Metadata: &podman.Metadata{ 161 Status: "exited", 162 Exited: true, 163 }, 164 Locations: []string{"bolt_state.db"}, 165 }, 166 { 167 Name: "docker.io/redis", 168 Version: "a8036f14f15ead9517115576fb4462894a000620c2be556410f6c24afb8a482b", 169 Metadata: &podman.Metadata{ 170 ExposedPorts: map[uint16][]string{6379: {"tcp"}}, 171 PID: 4232, 172 Status: "running", 173 }, 174 Locations: []string{"bolt_state.db"}, 175 }, 176 }, 177 }, 178 { 179 Name: "valid using bolt", 180 Path: "testdata/bolt_state.db", 181 WantPackages: []*extractor.Package{ 182 { 183 Name: "docker.io/redis", 184 Version: "a8036f14f15ead9517115576fb4462894a000620c2be556410f6c24afb8a482b", 185 Metadata: &podman.Metadata{ 186 ExposedPorts: map[uint16][]string{6379: {"tcp"}}, 187 PID: 4232, 188 Status: "running", 189 }, 190 Locations: []string{"bolt_state.db"}, 191 }, 192 }, 193 }, 194 } 195 196 for _, tt := range tests { 197 t.Run(tt.Name, func(t *testing.T) { 198 extr := podman.New(tt.Config) 199 200 // Move input to a tmp dir that SCALIBR has write access to 201 // (needed for bolt files which are opened using ). 202 d := t.TempDir() 203 dst := filepath.Join(d, filepath.Base(tt.Path)) 204 data, err := os.ReadFile(tt.Path) 205 if err != nil { 206 t.Fatalf("os.ReadFile(%q): %v", tt.Path, err) 207 } 208 err = os.WriteFile(dst, data, 0644) 209 if err != nil { 210 t.Fatalf("os.WriteFile(%q): %v", dst, err) 211 } 212 213 scanInput := &filesystem.ScanInput{ 214 FS: scalibrfs.DirFS(d), Path: filepath.Base(tt.Path), Reader: nil, Root: d, Info: nil, 215 } 216 217 got, err := extr.Extract(t.Context(), scanInput) 218 if diff := cmp.Diff(tt.WantErr, err, cmpopts.EquateErrors()); diff != "" { 219 t.Errorf("%s.Extract(%q) error diff (-want +got):\n%s", extr.Name(), tt.Path, diff) 220 return 221 } 222 223 opts := []cmp.Option{cmpopts.SortSlices(extracttest.PackageCmpLess), cmpopts.IgnoreTypes(time.Time{})} 224 if diff := cmp.Diff(tt.WantPackages, got.Packages, opts...); diff != "" { 225 t.Errorf("%s.Extract(%q) diff (-want +got):\n%s", extr.Name(), tt.Path, diff) 226 } 227 }) 228 } 229 }