github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/bom/docker/rpm.go (about) 1 package docker 2 3 import ( 4 "encoding/hex" 5 "fmt" 6 "io/ioutil" 7 "os" 8 9 rpmdb "github.com/anchore/go-rpmdb/pkg" 10 11 "github.com/vchain-us/vcn/pkg/bom/artifact" 12 "github.com/vchain-us/vcn/pkg/bom/executor" 13 ) 14 15 type rpm struct { 16 db *rpmdb.RpmDB 17 byFile map[string]*rpmdb.PackageInfo 18 } 19 20 var hashTypeMaps = map[rpmdb.DigestAlgorithm]artifact.HashType{ 21 rpmdb.PGPHASHALGO_MD5: artifact.HashMD5, 22 rpmdb.PGPHASHALGO_SHA1: artifact.HashSHA1, 23 rpmdb.PGPHASHALGO_MD2: artifact.HashMD2, 24 rpmdb.PGPHASHALGO_SHA256: artifact.HashSHA256, 25 rpmdb.PGPHASHALGO_SHA384: artifact.HashSHA384, 26 rpmdb.PGPHASHALGO_SHA512: artifact.HashSHA512, 27 rpmdb.PGPHASHALGO_SHA224: artifact.HashSHA224, 28 } 29 30 func (pkg rpm) Type() string { 31 return RPM 32 } 33 34 func (pkg *rpm) PackageByFile(e executor.Executor, file string, output artifact.OutputOptions) (artifact.Dependency, error) { 35 var err error 36 if pkg.db == nil { 37 pkg.db, err = openDb(e) 38 if err != nil { 39 return artifact.Dependency{}, err 40 } 41 } 42 43 if pkg.byFile == nil { 44 pkg.byFile = make(map[string]*rpmdb.PackageInfo) 45 pkgList, err := pkg.db.ListPackages() 46 if err != nil { 47 return artifact.Dependency{}, fmt.Errorf("cannot read RPM database: %w", err) 48 } 49 50 for i := range pkgList { 51 for _, f := range pkgList[i].Files { 52 pkg.byFile[f.Path] = pkgList[i] 53 } 54 } 55 } 56 57 p, ok := pkg.byFile[file] 58 if !ok { 59 return artifact.Dependency{}, ErrNotFound 60 } 61 62 hashtype, ok := hashTypeMaps[p.DigestAlgorithm] 63 if !ok { 64 hashtype = artifact.HashInvalid 65 } 66 hash, err := combineHashesFromSlice(p.Files, p.Name) 67 if err != nil { 68 fmt.Printf("Cannot combine hashes: %v\n", err) 69 // ignore error 70 } 71 if p.License == "" { 72 p.License = "NONE" 73 } 74 return artifact.Dependency{ 75 Name: p.Name, 76 Version: p.Version, 77 HashType: hashtype, 78 Hash: hash, 79 License: p.License, 80 }, nil 81 } 82 83 func (pkg *rpm) AllPackages(e executor.Executor, output artifact.OutputOptions) ([]artifact.Dependency, error) { 84 var err error 85 if pkg.db == nil { 86 pkg.db, err = openDb(e) 87 if err != nil { 88 return nil, err 89 } 90 } 91 92 pkgList, err := pkg.db.ListPackages() 93 if err != nil { 94 return nil, fmt.Errorf("cannot read RPM database: %w", err) 95 } 96 97 res := make([]artifact.Dependency, 0, len(pkgList)) 98 for _, p := range pkgList { 99 hashtype, ok := hashTypeMaps[p.DigestAlgorithm] 100 if !ok { 101 hashtype = artifact.HashInvalid 102 } 103 hash, err := combineHashesFromSlice(p.Files, p.Name) 104 if err != nil { 105 fmt.Printf("Cannot combine hashes: %v\n", err) 106 // ignore error 107 } 108 if hash == "" { 109 continue 110 } 111 if p.License == "" { 112 p.License = "NONE" 113 } 114 res = append(res, artifact.Dependency{ 115 Name: p.Name, 116 Version: p.Version, 117 HashType: hashtype, 118 Hash: hash, 119 License: p.License, 120 }) 121 } 122 123 return res, nil 124 } 125 126 func openDb(e executor.Executor) (*rpmdb.RpmDB, error) { 127 buf, err := e.ReadFile("/var/lib/rpm/Packages") 128 if err != nil { 129 return nil, fmt.Errorf("error reading file from container: %w", err) 130 } 131 132 f, err := ioutil.TempFile("", "vcn.rpmdb") 133 if err != nil { 134 return nil, fmt.Errorf("cannot create temporary file: %w", err) 135 } 136 rpmFile := f.Name() 137 defer os.Remove(rpmFile) // clean up 138 139 _, err = f.Write(buf) 140 if err != nil { 141 return nil, fmt.Errorf("cannot write temporary file: %w", err) 142 } 143 f.Close() 144 145 db, err := rpmdb.Open(rpmFile) 146 if err != nil { 147 return nil, fmt.Errorf("cannot read RPM database: %w", err) 148 } 149 150 return db, err 151 } 152 153 func combineHashesFromSlice(files []rpmdb.FileInfo, pkgName string) (string, error) { 154 var hash []byte 155 for _, file := range files { 156 if file.Digest == "" { 157 // some files don't have digests, like symbolic links 158 continue 159 } 160 comp, err := hex.DecodeString(file.Digest) 161 if err != nil { 162 return "", fmt.Errorf("malformed hash for package %s", pkgName) 163 } 164 if hash == nil { 165 hash = comp 166 } else { 167 if len(comp) != len(hash) { 168 // should never happen - all hashes must be of the same length 169 return "", fmt.Errorf("malformed hash for package %s", pkgName) 170 } 171 // XOR hash 172 for i := 0; i < len(hash); i++ { 173 hash[i] ^= comp[i] 174 } 175 } 176 } 177 178 return hex.EncodeToString(hash), nil 179 }