github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/bom/docker/docker.go (about) 1 /* 2 * Copyright (c) 2021 CodeNotary, Inc. All Rights Reserved. 3 * This software is released under GPL3. 4 * The full license information can be found under: 5 * https://www.gnu.org/licenses/gpl-3.0.en.html 6 * 7 */ 8 9 package docker 10 11 import ( 12 "bufio" 13 "bytes" 14 "crypto/sha256" 15 "encoding/hex" 16 "fmt" 17 "strings" 18 19 "github.com/schollz/progressbar/v3" 20 "github.com/vchain-us/vcn/pkg/bom/artifact" 21 "github.com/vchain-us/vcn/pkg/bom/executor" 22 ) 23 24 const AssetType = "docker" 25 26 const ( 27 APK = "apk" 28 DPKG = "dpkg" 29 RPM = "rpm" 30 ) 31 32 type DockerArtifact struct { 33 artifact.GenericArtifact 34 image string 35 binaries []string 36 ex executor.Executor 37 pkg pkgManager 38 pkgType string 39 } 40 41 // New returns new DockerArtifact object 42 // unlike other environments, this New() is not called from bom.New() 43 func New(path string, binaries []string) (*DockerArtifact, error) { 44 executor, err := executor.NewDockerExecutor(path) 45 if err != nil { 46 return nil, err 47 } 48 49 pkg, err := probePackageManager(executor) 50 if err != nil { 51 return nil, fmt.Errorf("error identifying package manager for the image: %w", err) 52 } 53 if pkg == nil { 54 return nil, fmt.Errorf("cannot identify package manager for the container image") 55 } 56 57 ret := DockerArtifact{ 58 image: path, 59 binaries: binaries, 60 ex: executor, 61 pkg: pkg, 62 pkgType: pkg.Type(), 63 } 64 return &ret, nil 65 } 66 67 func (p DockerArtifact) Type() string { 68 return p.pkgType 69 } 70 71 func (p DockerArtifact) Path() string { 72 return p.image 73 } 74 75 func (a *DockerArtifact) ResolveDependencies(output artifact.OutputOptions) ([]artifact.Dependency, error) { 76 if a.Deps != nil { 77 return a.Deps, nil 78 } 79 80 defer a.ex.Close() 81 82 var err error 83 result := make([]artifact.Dependency, 0) 84 if len(a.binaries) > 0 { 85 // for every binary find its dynamic libs, and find packages for these libs 86 var bar *progressbar.ProgressBar 87 if output == artifact.Progress { 88 // init progress indicator 89 bar = progressbar.Default(10) 90 } 91 92 packages := make(map[string]struct{}) 93 binCount := 0. 94 fileCount := 0. 95 for _, bin := range a.binaries { 96 stdOut, stdErr, exitCode, err := a.ex.Exec([]string{"ldd", bin}) 97 if err != nil { 98 return nil, fmt.Errorf("error starting 'ldd' command: %w", err) 99 } 100 101 if exitCode != 0 { 102 if stdErr == nil { 103 stdErr = stdOut 104 } 105 return nil, fmt.Errorf("error executing 'ldd' command: %s", stdErr) 106 } 107 108 if bar != nil { 109 scanner := bufio.NewScanner(bytes.NewBuffer(stdOut)) 110 for scanner.Scan() { 111 fileCount++ 112 } 113 binCount++ 114 projected := int(fileCount * float64(len(a.binaries)) / binCount) 115 bar.ChangeMax(projected) 116 } 117 118 scanner := bufio.NewScanner(bytes.NewBuffer(stdOut)) 119 for scanner.Scan() { 120 line := scanner.Text() 121 fields := strings.Split(line, " ") 122 var lib string 123 switch len(fields) { 124 case 2: 125 lib = strings.TrimSpace(fields[0]) 126 case 4: 127 lib = strings.TrimSpace(fields[2]) 128 default: 129 fmt.Printf("Ignoring malformed 'ldd' output: %s", line) 130 continue 131 } 132 133 if bar != nil { 134 bar.Add(1) 135 } 136 dep, err := a.pkg.PackageByFile(a.ex, lib, output) 137 if err == ErrNotFound { 138 // it is possible that lib is a symlink, in this case package manager can't find it, so resolve it and retry 139 stdOut, stdErr, exitCode, err = a.ex.Exec([]string{"readlink", "-f", lib}) 140 if err != nil { 141 return nil, fmt.Errorf("error starting 'readlink' command: %w", err) 142 } 143 if exitCode != 0 { 144 if stdErr == nil { 145 stdErr = stdOut 146 } 147 return nil, fmt.Errorf("error executing 'readlink' command: %s", stdErr) 148 } 149 lib = strings.TrimSpace(string(stdOut)) 150 dep, err = a.pkg.PackageByFile(a.ex, lib, output) 151 if err != nil { 152 // it is ok when library is not a part of the package - ignore 153 continue 154 } 155 } else if err != nil { 156 // it is ok when library is not a part of the package - ignore 157 continue 158 } 159 if _, ok := packages[dep.Name]; ok { 160 continue // package already processed 161 } 162 packages[dep.Name] = struct{}{} 163 result = append(result, dep) 164 } 165 } 166 } else { 167 result, err = a.pkg.AllPackages(a.ex, output) 168 if err != nil { 169 return nil, err 170 } 171 } 172 173 a.Deps = result 174 return result, nil 175 } 176 177 func (a *DockerArtifact) FileHash(name string) (string, error) { 178 buf, err := a.ex.ReadFile(name) 179 if err != nil { 180 return "", nil 181 } 182 183 checksum := sha256.Sum256(buf) 184 return hex.EncodeToString(checksum[:]), nil 185 }