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  }