github.com/anchore/syft@v1.38.2/syft/pkg/cataloger/nix/db_cataloger_v10.go (about) 1 package nix 2 3 import ( 4 "database/sql" 5 "fmt" 6 "io" 7 "os" 8 9 "github.com/anchore/syft/internal" 10 "github.com/anchore/syft/internal/log" 11 "github.com/anchore/syft/syft/artifact" 12 "github.com/anchore/syft/syft/file" 13 "github.com/anchore/syft/syft/pkg" 14 ) 15 16 var _ dbProcessor = processV10DB 17 18 func processV10DB(config Config, dbLocation file.Location, resolver file.Resolver, catalogerName string) ([]pkg.Package, []artifact.Relationship, error) { 19 dbContents, err := resolver.FileContentsByLocation(dbLocation) 20 defer internal.CloseAndLogError(dbContents, dbLocation.RealPath) 21 if err != nil { 22 return nil, nil, fmt.Errorf("unable to read Nix database: %w", err) 23 } 24 25 tempDB, err := createTempDB(dbContents) 26 if err != nil { 27 return nil, nil, fmt.Errorf("failed to create temporary database: %w", err) 28 } 29 defer os.RemoveAll(tempDB.Name()) 30 31 db, err := sql.Open("sqlite", tempDB.Name()) 32 if err != nil { 33 return nil, nil, fmt.Errorf("failed to open database: %w", err) 34 } 35 36 db.SetConnMaxLifetime(0) 37 defer db.Close() 38 39 packageEntries, err := extractV10DBPackages(config, db, dbLocation, resolver) 40 if err != nil { 41 return nil, nil, err 42 } 43 44 pkgs, relationships, err := finalizeV10DBResults(db, packageEntries, catalogerName) 45 if err != nil { 46 return nil, nil, err 47 } 48 49 return pkgs, relationships, nil 50 } 51 52 func extractV10DBPackages(config Config, db *sql.DB, dbLocation file.Location, resolver file.Resolver) (map[int]*dbPackageEntry, error) { 53 pkgs, err := extractV10DBValidPaths(config, db, dbLocation, resolver) 54 if err != nil { 55 return nil, err 56 } 57 58 err = extractV10DBDerivationOutputs(db, pkgs) 59 if err != nil { 60 return nil, err 61 } 62 63 return pkgs, nil 64 } 65 66 func extractV10DBValidPaths(config Config, db *sql.DB, dbLocation file.Location, resolver file.Resolver) (map[int]*dbPackageEntry, error) { 67 packages := make(map[int]*dbPackageEntry) 68 69 rows, err := db.Query("SELECT id, path, hash, deriver FROM ValidPaths") 70 if err != nil { 71 return nil, fmt.Errorf("failed to query ValidPaths: %w", err) 72 } 73 defer rows.Close() 74 75 for rows.Next() { 76 var id int 77 var path, hash, deriver sql.NullString 78 79 if err := rows.Scan(&id, &path, &hash, &deriver); err != nil { 80 return nil, fmt.Errorf("failed to scan ValidPaths row: %w", err) 81 } 82 83 if !path.Valid { 84 continue 85 } 86 87 nsp := parseNixStorePath(path.String) 88 if nsp == nil { 89 nsp = &nixStorePath{} 90 } 91 // always trust the DB values over string parsing 92 nsp.OutputHash = hash.String 93 nsp.StorePath = path.String 94 95 var files []string 96 if config.CaptureOwnedFiles { 97 files = listOutputPaths(path.String, resolver) 98 } 99 100 df, err := newDerivationFromPath(deriver.String, resolver) 101 if err != nil { 102 log.WithFields("path", deriver.String, "error", err).Trace("unable to find derivation") 103 df = nil 104 } 105 106 packages[id] = &dbPackageEntry{ 107 ID: id, 108 nixStorePath: *nsp, 109 derivationFile: df, 110 DeriverPath: deriver.String, 111 Location: &dbLocation, 112 Files: files, 113 } 114 } 115 116 return packages, nil 117 } 118 119 func listOutputPaths(storePath string, resolver file.Resolver) []string { 120 if storePath == "" { 121 return nil 122 } 123 searchGlob := storePath + "/**/*" 124 locations, err := resolver.FilesByGlob(searchGlob) 125 if err != nil { 126 log.WithFields("path", storePath, "error", err).Trace("unable to find output paths") 127 return nil 128 } 129 130 return filePaths(locations) 131 } 132 133 func extractV10DBDerivationOutputs(db *sql.DB, packages map[int]*dbPackageEntry) error { 134 outputRows, err := db.Query("SELECT drv, id, path FROM DerivationOutputs") 135 if err != nil { 136 return fmt.Errorf("failed to query DerivationOutputs: %w", err) 137 } 138 defer outputRows.Close() 139 140 pkgsByPath := make(map[string]*dbPackageEntry) 141 for _, p := range packages { 142 pkgsByPath[p.StorePath] = p 143 } 144 145 for outputRows.Next() { 146 var drvID int 147 var outputID, outputPath string 148 149 if err := outputRows.Scan(&drvID, &outputID, &outputPath); err != nil { 150 return fmt.Errorf("failed to scan DerivationOutputs row: %w", err) 151 } 152 153 if _, ok := pkgsByPath[outputPath]; !ok { 154 continue 155 } 156 pkgsByPath[outputPath].Output = outputID 157 pkgsByPath[outputPath].DrvID = drvID 158 } 159 160 return nil 161 } 162 163 func finalizeV10DBResults(db *sql.DB, packageEntries map[int]*dbPackageEntry, catalogerName string) ([]pkg.Package, []artifact.Relationship, error) { 164 // make Syft packages for each package entry 165 syftPackages := make(map[int]pkg.Package) 166 for id, entry := range packageEntries { 167 syftPackages[id] = newDBPackage(entry, catalogerName) 168 } 169 170 var relationships []artifact.Relationship 171 172 query := ` 173 SELECT r.referrer, r.reference 174 FROM Refs r 175 JOIN ValidPaths v1 ON r.referrer = v1.id 176 JOIN ValidPaths v2 ON r.reference = v2.id 177 ` 178 179 refRows, err := db.Query(query) 180 if err != nil { 181 return nil, nil, fmt.Errorf("failed to query Refs with ValidPaths JOIN: %w", err) 182 } 183 defer refRows.Close() 184 185 relExists := make(map[int]map[int]bool) 186 187 for refRows.Next() { 188 var referrerID, referenceID int 189 190 if err := refRows.Scan(&referrerID, &referenceID); err != nil { 191 return nil, nil, fmt.Errorf("failed to scan Refs row: %w", err) 192 } 193 194 if referrerID == referenceID { 195 // skip self-references 196 continue 197 } 198 199 referrer, refExists := syftPackages[referrerID] 200 reference, refeeExists := syftPackages[referenceID] 201 202 if !refExists || !refeeExists { 203 // only include relationships for packages we have discovered 204 continue 205 } 206 207 if _, ok := relExists[referrerID]; !ok { 208 relExists[referrerID] = make(map[int]bool) 209 } 210 211 if relExists[referrerID][referenceID] { 212 // deduplicate existing relationships 213 continue 214 } 215 216 relExists[referrerID][referenceID] = true 217 218 rel := artifact.Relationship{ 219 From: reference, 220 To: referrer, 221 Type: artifact.DependencyOfRelationship, 222 } 223 224 relationships = append(relationships, rel) 225 } 226 227 var pkgs []pkg.Package 228 for _, p := range syftPackages { 229 pkgs = append(pkgs, p) 230 } 231 232 return pkgs, relationships, nil 233 } 234 235 func createTempDB(content io.ReadCloser) (*os.File, error) { 236 tempFile, err := os.CreateTemp("", "nix-db-*.sqlite") 237 if err != nil { 238 return nil, err 239 } 240 241 _, err = io.Copy(tempFile, content) 242 if err != nil { 243 tempFile.Close() 244 os.Remove(tempFile.Name()) 245 return nil, err 246 } 247 248 return tempFile, nil 249 }