github.com/google/osv-scalibr@v0.4.1/extractor/filesystem/os/nix/nix_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 nix_test 16 17 import ( 18 "io/fs" 19 "os" 20 "path/filepath" 21 "testing" 22 23 "github.com/google/go-cmp/cmp" 24 "github.com/google/osv-scalibr/extractor" 25 "github.com/google/osv-scalibr/extractor/filesystem" 26 "github.com/google/osv-scalibr/extractor/filesystem/os/nix" 27 nixmeta "github.com/google/osv-scalibr/extractor/filesystem/os/nix/metadata" 28 "github.com/google/osv-scalibr/extractor/filesystem/simplefileapi" 29 scalibrfs "github.com/google/osv-scalibr/fs" 30 "github.com/google/osv-scalibr/inventory" 31 "github.com/google/osv-scalibr/purl" 32 "github.com/google/osv-scalibr/stats" 33 "github.com/google/osv-scalibr/testing/fakefs" 34 ) 35 36 func TestFileRequired(t *testing.T) { 37 tests := []struct { 38 name string 39 path string 40 fileSizeBytes int64 41 maxFileSizeBytes int64 42 wantRequired bool 43 wantResultMetric stats.FileRequiredResult 44 }{ 45 { 46 name: "nix/store directory", 47 path: "nix/store/xakcaxsqdzjszym0vji2r8n0wdy2inqc-perl5.38.2-FCGI-ProcManager-0.28/xxx", 48 wantRequired: true, 49 wantResultMetric: stats.FileRequiredResultOK, 50 }, 51 { 52 name: "invalid nix/store directory", 53 path: "nix/xxx/xakcaxsqdzjszym0vji2r8n0wdy2inqc-perl5.38.2-FCGI-ProcManager-0.28/xxx", 54 wantRequired: false, 55 }, 56 { 57 name: "invalid nix/store directory", 58 path: "nix/storefoo/sss", 59 wantRequired: false, 60 }, 61 { 62 name: "nix/store directory already parsed", 63 path: "nix/store/xakcaxsqdzjszym0vji2r8n0wdy2inqc-perl5.38.2-FCGI-ProcManager-0.28/sss", 64 wantRequired: false, 65 }, 66 { 67 name: "no nix/store prefix", 68 path: "foo/store/xakcaxsqdzjszym0vji2r8n0wdy2inqc-perl5.38.2-FCGI-ProcManager-0.28/sss", 69 wantRequired: false, 70 }, 71 } 72 73 var e = nix.New() 74 75 for _, tt := range tests { 76 t.Run(tt.name, func(t *testing.T) { 77 isRequired := e.FileRequired(simplefileapi.New(tt.path, fakefs.FakeFileInfo{ 78 FileName: filepath.Base(tt.path), 79 FileMode: fs.ModePerm, 80 })) 81 82 if isRequired != tt.wantRequired { 83 t.Fatalf("FileRequired(%s): got %v, want %v", tt.path, isRequired, tt.wantRequired) 84 } 85 }) 86 } 87 } 88 89 func TestExtract(t *testing.T) { 90 const NixVicuna = `ANSI_COLOR="1;34" 91 BUG_REPORT_URL="https://github.com/NixOS/nixpackages/issues" 92 BUILD_ID="24.11.710315.b681065d0919" 93 CPE_NAME="cpe:/o:nixos:nixos:24.11" 94 DEFAULT_HOSTNAME=nixos 95 DOCUMENTATION_URL="https://nixos.org/learn.html" 96 HOME_URL="https://nixos.org/" 97 ID=nixos 98 ID_LIKE="" 99 IMAGE_ID="" 100 IMAGE_VERSION="" 101 LOGO="nix-snowflake" 102 NAME=NixOS 103 PRETTY_NAME="NixOS 24.11 (Vicuna)" 104 SUPPORT_END="2025-06-30" 105 SUPPORT_URL="https://nixos.org/community.html" 106 VARIANT="" 107 VARIANT_ID="" 108 VENDOR_NAME="NixOS" 109 VENDOR_URL="https://nixos.org/" 110 VERSION="24.11 (Vicuna)" 111 VERSION_CODENAME=vicuna 112 VERSION_ID="24.11"` 113 114 tests := []struct { 115 name string 116 path string 117 osrelease string 118 wantPackages []*extractor.Package 119 wantError error 120 wantResultMetric stats.FileExtractedResult 121 }{ 122 { 123 name: "valid", 124 path: "nix/store/xakcaxsqdzjszym0vji2r8n0wdy2inqc-perl5.38.2-FCGI-ProcManager-0.28/foo", 125 osrelease: NixVicuna, 126 wantPackages: []*extractor.Package{ 127 { 128 Name: "perl5.38.2-FCGI-ProcManager", 129 Version: "0.28", 130 PURLType: purl.TypeNix, 131 Metadata: &nixmeta.Metadata{ 132 PackageName: "perl5.38.2-FCGI-ProcManager", 133 PackageVersion: "0.28", 134 PackageHash: "xakcaxsqdzjszym0vji2r8n0wdy2inqc", 135 PackageOutput: "", 136 OSID: "nixos", 137 OSVersionCodename: "vicuna", 138 OSVersionID: "24.11", 139 }, 140 Locations: []string{"nix/store/xakcaxsqdzjszym0vji2r8n0wdy2inqc-perl5.38.2-FCGI-ProcManager-0.28/foo"}, 141 }, 142 }, 143 wantResultMetric: stats.FileExtractedResultSuccess, 144 }, 145 { 146 name: "valid", 147 path: "nix/store/q5dhwzcn82by5ndc7g0q83wsnn13qkqw-webdav-server-rs-unstable-2021-08-16/foo", 148 osrelease: NixVicuna, 149 wantPackages: []*extractor.Package{ 150 { 151 Name: "webdav-server-rs", 152 Version: "unstable-2021-08-16", 153 PURLType: purl.TypeNix, 154 Metadata: &nixmeta.Metadata{ 155 PackageName: "webdav-server-rs", 156 PackageVersion: "unstable-2021-08-16", 157 PackageHash: "q5dhwzcn82by5ndc7g0q83wsnn13qkqw", 158 PackageOutput: "", 159 OSID: "nixos", 160 OSVersionCodename: "vicuna", 161 OSVersionID: "24.11", 162 }, 163 Locations: []string{"nix/store/q5dhwzcn82by5ndc7g0q83wsnn13qkqw-webdav-server-rs-unstable-2021-08-16/foo"}, 164 }, 165 }, 166 wantResultMetric: stats.FileExtractedResultSuccess, 167 }, 168 { 169 name: "invalid package hash", 170 path: "nix/store/foo-webdav-server-rs-unstable-2021-08-16/foo", 171 osrelease: NixVicuna, 172 wantPackages: nil, 173 }, 174 { 175 name: "no package name", 176 path: "nix/store/xakcaxsqdzjszym0vji2r8n0wdy2inqc-0.28/foo", 177 osrelease: NixVicuna, 178 wantPackages: nil, 179 }, 180 { 181 name: "no package version", 182 path: "nix/store/xakcaxsqdzjszym0vji2r8n0wdy2inqc-perl5.38.2-FCGI-ProcManager/foo", 183 osrelease: NixVicuna, 184 wantPackages: nil, 185 }, 186 { 187 name: "invalid", 188 path: "nix/store/xzlmnp0lblcbscy36nlgif3js4mc68gm-base-system/etc/group", 189 osrelease: NixVicuna, 190 wantPackages: nil, 191 }, 192 { 193 name: "invalid", 194 path: "nix/store/a-b-c-d-e/foo", 195 osrelease: NixVicuna, 196 wantPackages: nil, 197 }, 198 } 199 200 for _, tt := range tests { 201 t.Run(tt.name, func(t *testing.T) { 202 var e = nix.New() 203 204 d := t.TempDir() 205 createOsRelease(t, d, tt.osrelease) 206 207 input := &filesystem.ScanInput{ 208 FS: scalibrfs.DirFS(d), 209 Path: tt.path, 210 Reader: nil, 211 Root: d, 212 Info: nil, 213 } 214 215 got, err := e.Extract(t.Context(), input) 216 217 if err != nil { 218 t.Errorf("err = %v", err) 219 } 220 221 wantInv := inventory.Inventory{Packages: tt.wantPackages} 222 if diff := cmp.Diff(wantInv, got); diff != "" { 223 t.Errorf("Package mismatch (-want +got):\n%s", diff) 224 } 225 }) 226 } 227 } 228 229 func createOsRelease(t *testing.T, root string, content string) { 230 t.Helper() 231 _ = os.MkdirAll(filepath.Join(root, "etc"), 0755) 232 err := os.WriteFile(filepath.Join(root, "etc/os-release"), []byte(content), 0644) 233 if err != nil { 234 t.Fatalf("write to %s: %v\n", filepath.Join(root, "etc/os-release"), err) 235 } 236 }