github.com/jfrog/jfrog-cli-go@v1.22.1-0.20200318093948-4826ef344ffd/xray/commands/offlineupdate.go (about) 1 package commands 2 3 import ( 4 "encoding/json" 5 "errors" 6 "fmt" 7 "github.com/jfrog/jfrog-cli-go/utils/cliutils" 8 "github.com/jfrog/jfrog-client-go/httpclient" 9 "github.com/jfrog/jfrog-client-go/utils" 10 "github.com/jfrog/jfrog-client-go/utils/errorutils" 11 "github.com/jfrog/jfrog-client-go/utils/io/fileutils" 12 "github.com/jfrog/jfrog-client-go/utils/io/httputils" 13 "github.com/jfrog/jfrog-client-go/utils/log" 14 "io/ioutil" 15 "net/http" 16 "os" 17 "path/filepath" 18 "strconv" 19 "strings" 20 ) 21 22 const ( 23 Vulnerability = "__vuln" 24 Component = "__comp" 25 JxrayDefaultBaseUrl = "https://jxray.jfrog.io/" 26 JxrayApiBundles = "api/v1/updates/bundles" 27 JxrayApiOnboarding = "api/v1/updates/onboarding" 28 ) 29 30 func OfflineUpdate(flags *OfflineUpdatesFlags) error { 31 updatesUrl, err := buildUpdatesUrl(flags) 32 if err != nil { 33 return err 34 } 35 vulnerabilities, components, lastUpdate, err := getFilesList(updatesUrl, flags) 36 if err != nil { 37 return err 38 } 39 zipSuffix := "_" + strconv.FormatInt(lastUpdate, 10) 40 xrayTempDir, err := getXrayTempDir() 41 if err != nil { 42 return err 43 } 44 45 if flags.Target != "" && (len(vulnerabilities) > 0 || len(components) > 0) { 46 err = os.MkdirAll(flags.Target, 0777) 47 if errorutils.CheckError(err) != nil { 48 return err 49 } 50 } 51 52 if len(vulnerabilities) > 0 { 53 log.Info("Downloading vulnerabilities...") 54 err := saveData(xrayTempDir, "vuln", zipSuffix, flags.Target, vulnerabilities) 55 if err != nil { 56 return err 57 } 58 } else { 59 log.Info("There are no new vulnerabilities.") 60 } 61 62 if len(components) > 0 { 63 log.Info("Downloading components...") 64 err := saveData(xrayTempDir, "comp", zipSuffix, flags.Target, components) 65 if err != nil { 66 return err 67 } 68 } else { 69 log.Info("There are no new components.") 70 } 71 72 return nil 73 } 74 75 func getUpdatesBaseUrl(datesSpecified bool) string { 76 jxRayBaseUrl := os.Getenv("JFROG_CLI_JXRAY_BASE_URL") 77 jxRayBaseUrl = utils.AddTrailingSlashIfNeeded(jxRayBaseUrl) 78 if jxRayBaseUrl == "" { 79 jxRayBaseUrl = JxrayDefaultBaseUrl 80 } 81 if datesSpecified { 82 return jxRayBaseUrl + JxrayApiBundles 83 } 84 return jxRayBaseUrl + JxrayApiOnboarding 85 } 86 87 func buildUpdatesUrl(flags *OfflineUpdatesFlags) (string, error) { 88 var queryParams string 89 datesSpecified := flags.From > 0 && flags.To > 0 90 if datesSpecified { 91 if err := validateDates(flags.From, flags.To); err != nil { 92 return "", err 93 } 94 queryParams += fmt.Sprintf("from=%v&to=%v", flags.From, flags.To) 95 } 96 if flags.Version != "" { 97 if queryParams != "" { 98 queryParams += "&" 99 } 100 queryParams += fmt.Sprintf("version=%v", flags.Version) 101 } 102 url := getUpdatesBaseUrl(datesSpecified) 103 if queryParams != "" { 104 url += "?" + queryParams 105 } 106 return url, nil 107 } 108 109 func validateDates(from, to int64) error { 110 if from < 0 || to < 0 { 111 err := errors.New("Invalid dates") 112 return errorutils.CheckError(err) 113 } 114 if from > to { 115 err := errors.New("Invalid dates range.") 116 return errorutils.CheckError(err) 117 } 118 return nil 119 } 120 121 func getXrayTempDir() (string, error) { 122 xrayDir := filepath.Join(cliutils.GetCliPersistentTempDirPath(), "jfrog", "xray") 123 if err := os.MkdirAll(xrayDir, 0777); err != nil { 124 errorutils.CheckError(err) 125 return "", nil 126 } 127 return xrayDir, nil 128 } 129 130 func saveData(xrayTmpDir, filesPrefix, zipSuffix, targetPath string, urlsList []string) error { 131 dataDir, err := ioutil.TempDir(xrayTmpDir, filesPrefix) 132 if err != nil { 133 return err 134 } 135 defer func() { 136 if cerr := os.RemoveAll(dataDir); cerr != nil && err == nil { 137 err = cerr 138 } 139 }() 140 for _, url := range urlsList { 141 fileName, err := createXrayFileNameFromUrl(url) 142 if err != nil { 143 return err 144 } 145 log.Info("Downloading", url) 146 client, err := httpclient.ClientBuilder().Build() 147 if err != nil { 148 return err 149 } 150 151 details := &httpclient.DownloadFileDetails{ 152 FileName: fileName, 153 DownloadPath: url, 154 LocalPath: dataDir, 155 LocalFileName: fileName} 156 _, err = client.DownloadFile(details, "", httputils.HttpClientDetails{}, 3, false) 157 if err != nil { 158 return err 159 } 160 } 161 log.Info("Zipping files.") 162 err = fileutils.ZipFolderFiles(dataDir, filepath.Join(targetPath, filesPrefix+zipSuffix+".zip")) 163 if err != nil { 164 return err 165 } 166 log.Info("Done zipping files.") 167 return nil 168 } 169 170 func createXrayFileNameFromUrl(url string) (fileName string, err error) { 171 originalUrl := url 172 index := strings.Index(url, "?") 173 if index != -1 { 174 url = url[:index] 175 } 176 index = strings.Index(url, ";") 177 if index != -1 { 178 url = url[:index] 179 } 180 181 sections := strings.Split(url, "/") 182 length := len(sections) 183 if length < 2 { 184 err = errorutils.CheckError(errors.New(fmt.Sprintf("Unexpected URL format: %s", originalUrl))) 185 return 186 } 187 fileName = fmt.Sprintf("%s__%s", sections[length-2], sections[length-1]) 188 return 189 } 190 191 func getFilesList(updatesUrl string, flags *OfflineUpdatesFlags) (vulnerabilities []string, components []string, lastUpdate int64, err error) { 192 log.Info("Getting updates...") 193 headers := make(map[string]string) 194 headers["X-Xray-License"] = flags.License 195 httpClientDetails := httputils.HttpClientDetails{ 196 Headers: headers, 197 } 198 client, err := httpclient.ClientBuilder().Build() 199 if err != nil { 200 return 201 } 202 resp, body, _, err := client.SendGet(updatesUrl, false, httpClientDetails) 203 if errorutils.CheckError(err) != nil { 204 return 205 } 206 if err = errorutils.CheckResponseStatus(resp, http.StatusOK); err != nil { 207 err = errorutils.CheckError(errors.New("Response: " + err.Error())) 208 return 209 } 210 211 var urls FilesList 212 err = json.Unmarshal(body, &urls) 213 if err != nil { 214 err = errorutils.CheckError(errors.New("Failed parsing json response: " + string(body))) 215 return 216 } 217 218 for _, v := range urls.Urls { 219 if strings.Contains(v, Vulnerability) { 220 vulnerabilities = append(vulnerabilities, v) 221 } else if strings.Contains(v, Component) { 222 components = append(components, v) 223 } 224 } 225 lastUpdate = urls.Last_update 226 return 227 } 228 229 type OfflineUpdatesFlags struct { 230 License string 231 From int64 232 To int64 233 Version string 234 Target string 235 } 236 237 type FilesList struct { 238 Last_update int64 239 Urls []string 240 }