From 5254513a6a30ff054e9adced69755ec5613ad497 Mon Sep 17 00:00:00 2001 From: Deepak Sharma Date: Wed, 17 Mar 2021 04:35:50 -0700 Subject: [PATCH] feat: add report link in summary and analyse command (#34) * link in summary output * link in verbose output * link to server * tests * review --- analyses/driver/matcher.go | 1 + analyses/stackanalyses/controller.go | 1 + analyses/summary/helper_test.go | 2 ++ analyses/summary/helpers.go | 5 +++- analyses/summary/types.go | 21 ++++++------- analyses/verbose/helper.go | 3 ++ analyses/verbose/testdata/getresponse.json | 3 +- analyses/verbose/testdata/verbosedata.json | 3 +- analyses/verbose/types.go | 1 + utils/api.go | 34 ++++++++++++++++------ utils/constants.go | 7 +++-- 11 files changed, 56 insertions(+), 25 deletions(-) diff --git a/analyses/driver/matcher.go b/analyses/driver/matcher.go index 88b6e01..f337df3 100644 --- a/analyses/driver/matcher.go +++ b/analyses/driver/matcher.go @@ -51,6 +51,7 @@ type AnalysedDepsType struct { type GetResponseType struct { AnalysedDeps []AnalysedDepsType `json:"analyzed_dependencies"` RegistrationStatus string `json:"registration_status"` + StackID string `json:"external_request_id"` } // ReadManifestResponse is arg type of readManifest func diff --git a/analyses/stackanalyses/controller.go b/analyses/stackanalyses/controller.go index 8592d46..fd5e26b 100644 --- a/analyses/stackanalyses/controller.go +++ b/analyses/stackanalyses/controller.go @@ -70,6 +70,7 @@ func (mc *Controller) postRequest(requestParams driver.RequestType, filePath str Endpoint: APIStackAnalyses, ThreeScaleToken: requestParams.ThreeScaleToken, Host: requestParams.Host, + UserID: requestParams.UserID, } writer := multipart.NewWriter(manifest) fd, err := os.Open(filePath) diff --git a/analyses/summary/helper_test.go b/analyses/summary/helper_test.go index 2ab3e39..c4766eb 100644 --- a/analyses/summary/helper_test.go +++ b/analyses/summary/helper_test.go @@ -24,6 +24,7 @@ func data() driver.GetResponseType { }, }, }, + StackID: "123456789", } return *GetResponse } @@ -48,6 +49,7 @@ func TestGetResultSummary(t *testing.T) { HighVulnerabilities: 0, MediumVulnerabilities: 1, LowVulnerabilities: 0, + ReportLink: "https://recommender.api.openshift.io/api/v2/stack-report/123456789", } if diff := cmp.Diff(want, got); diff != "" { t.Errorf("Vuln mismatch (-want, +got):\n%s", diff) diff --git a/analyses/summary/helpers.go b/analyses/summary/helpers.go index f07a0e6..addc6fe 100644 --- a/analyses/summary/helpers.go +++ b/analyses/summary/helpers.go @@ -9,6 +9,7 @@ import ( "github.com/rs/zerolog/log" "github.com/fabric8-analytics/cli-tools/analyses/driver" + "github.com/fabric8-analytics/cli-tools/utils" ) // ProcessSummary processes summary results, return true if Vul found @@ -37,6 +38,7 @@ func getResultSummary(analysedResult driver.GetResponseType) *StackSummary { HighVulnerabilities: data.Severities.High, MediumVulnerabilities: data.Severities.Medium, LowVulnerabilities: data.Severities.Low, + ReportLink: utils.BuildReportLink(analysedResult.StackID), } return out } @@ -109,11 +111,12 @@ func outputSummaryPlain(result *StackSummary, verboseMsg bool) { white("Direct Vulnerable Dependencies: "), white(result.DirectVulnerableDependencies), "\n", white("Total Vulnerabilities: "), white(result.TotalVulnerabilities), "\n", white("Commonly Known Vulnerabilities: "), white(result.CommonlyKnownVulnerabilities), "\n", - white("Vulnerabilities Unique to Synk: "), white(result.VulnerabilitiesUniqueToSynk), "\n", + white("Vulnerabilities Unique to Snyk: "), white(result.VulnerabilitiesUniqueToSynk), "\n", red("Critical Vulnerabilities: "), red(result.CriticalVulnerabilities), "\n", magenta("High Vulnerabilities: "), magenta(result.HighVulnerabilities), "\n", yellow("Medium Vulnerabilities: "), yellow(result.MediumVulnerabilities), "\n", blue("Low Vulnerabilities: "), blue(result.LowVulnerabilities), "\n\n", + white("Full Report: "), result.ReportLink, "\n\n", ) fmt.Fprint(os.Stdout, "(Powered by Snyk)\n\n") if verboseMsg { diff --git a/analyses/summary/types.go b/analyses/summary/types.go index 96559f7..d769977 100644 --- a/analyses/summary/types.go +++ b/analyses/summary/types.go @@ -10,16 +10,17 @@ type SeverityType struct { // StackSummary is SA Result Summary output type StackSummary struct { - TotalScannedDependencies int `json:"total_scanned_dependencies"` - TotalScannedTransitiveDependencies int `json:"total_scanned_transitives"` - TotalVulnerabilities int `json:"total_vulnerabilites"` - CommonlyKnownVulnerabilities int `json:"commonly_known_vulnerabilites"` - VulnerabilitiesUniqueToSynk int `json:"vulnerabilities_unique_to_synk"` - DirectVulnerableDependencies int `json:"direct_vulnerable_dependencies"` - LowVulnerabilities int `json:"low_vulnerabilities"` - MediumVulnerabilities int `json:"medium_vulnerabilities"` - HighVulnerabilities int `json:"high_vulnerabilities"` - CriticalVulnerabilities int `json:"critical_vulnerabilities"` + TotalScannedDependencies int `json:"total_scanned_dependencies"` + TotalScannedTransitiveDependencies int `json:"total_scanned_transitives"` + TotalVulnerabilities int `json:"total_vulnerabilites"` + CommonlyKnownVulnerabilities int `json:"commonly_known_vulnerabilites"` + VulnerabilitiesUniqueToSynk int `json:"vulnerabilities_unique_to_synk"` + DirectVulnerableDependencies int `json:"direct_vulnerable_dependencies"` + LowVulnerabilities int `json:"low_vulnerabilities"` + MediumVulnerabilities int `json:"medium_vulnerabilities"` + HighVulnerabilities int `json:"high_vulnerabilities"` + CriticalVulnerabilities int `json:"critical_vulnerabilities"` + ReportLink string `json:"report_link"` } // ProcessVulnerabilities is arg type of processVulnerabilities diff --git a/analyses/verbose/helper.go b/analyses/verbose/helper.go index 1728787..19683e8 100644 --- a/analyses/verbose/helper.go +++ b/analyses/verbose/helper.go @@ -3,6 +3,7 @@ package verbose import ( "encoding/json" "fmt" + "github.com/fabric8-analytics/cli-tools/utils" "os" "sort" @@ -39,6 +40,7 @@ func getVerboseResult(analysedResult driver.GetResponseType) *StackVerbose { TotalDirectVulnerabilities: data.TotalDirectVulnerabilities, TotalTransitiveVulnerabilities: data.TotalTransitiveVulnerabilities, Severity: data.Severities, + ReportLink: utils.BuildReportLink(analysedResult.StackID), } return out } @@ -143,6 +145,7 @@ func outputVerbosePlain(result *StackVerbose) { ) fmt.Fprintln(os.Stdout, cusColor.Green("Fixable Issues:")) outputVulDeps(result.Dependencies) + fmt.Fprint(os.Stdout, fmt.Sprintf(cusColor.White("\n\n Full Report: ")+"%s \n\n", result.ReportLink)) fmt.Fprint(os.Stdout, "\n(Powered by Snyk)\n\n") } diff --git a/analyses/verbose/testdata/getresponse.json b/analyses/verbose/testdata/getresponse.json index 39984a7..b59f0a4 100644 --- a/analyses/verbose/testdata/getresponse.json +++ b/analyses/verbose/testdata/getresponse.json @@ -35,5 +35,6 @@ } ] } - ] + ], + "external_request_id": "123456789" } \ No newline at end of file diff --git a/analyses/verbose/testdata/verbosedata.json b/analyses/verbose/testdata/verbosedata.json index c94d865..7487592 100644 --- a/analyses/verbose/testdata/verbosedata.json +++ b/analyses/verbose/testdata/verbosedata.json @@ -34,5 +34,6 @@ "critical": [ {"id": "Critical-12345", "severity": "critical", "title": "SQL Attack"} ] - } + }, + "report_link": "https://recommender.api.openshift.io/api/v2/stack-report/123456789" } \ No newline at end of file diff --git a/analyses/verbose/types.go b/analyses/verbose/types.go index 7d0f0ca..6289873 100644 --- a/analyses/verbose/types.go +++ b/analyses/verbose/types.go @@ -11,6 +11,7 @@ type StackVerbose struct { TotalTransitives int `json:"total_transitives_scanned"` TotalTransitiveVulnerabilities int `json:"transitive_vulnerabilities"` Severity SeverityType `json:"severity"` + ReportLink string `json:"report_link"` } // VulnerabilityType type for Vulnerability in verbose output diff --git a/utils/api.go b/utils/api.go index 1583b1b..4257a26 100644 --- a/utils/api.go +++ b/utils/api.go @@ -3,6 +3,7 @@ package utils import ( "bytes" "encoding/json" + "fmt" "mime/multipart" "net/http" "net/url" @@ -23,6 +24,20 @@ type HTTPRequestType struct { UserID string `json:"user_id,omitempty"` } +// BuildReportLink builds stack report UI Link +func BuildReportLink(stackID string) string { + log.Debug().Msgf("Building Report Url.") + APIHost, err := url.Parse(ActualHost) + if err != nil { + log.Fatal().Err(err).Msgf("Unable to Parse Host URL") + } + endpoint := fmt.Sprintf("api/v2/stack-report/%s", stackID) + reportURL := url.URL{Host: APIHost.Hostname(), Path: endpoint} + reportURL.Scheme = "https" + log.Debug().Msgf("Success Building Report Url.") + return reportURL.String() +} + // buildAPIURL builds API Endpoint URL func buildAPIURL(host string, endpoint string, threeScale string) url.URL { log.Debug().Msgf("Building API Url.") @@ -30,22 +45,22 @@ func buildAPIURL(host string, endpoint string, threeScale string) url.URL { if err != nil { log.Fatal().Err(err).Msgf("Unable to Parse Host URL") } - url := url.URL{Host: APIHost.Hostname(), Path: endpoint} - url.Scheme = "https" - q := url.Query() + apiURL := url.URL{Host: APIHost.Hostname(), Path: endpoint} + apiURL.Scheme = "https" + q := apiURL.Query() q.Set("user_key", threeScale) - url.RawQuery = q.Encode() + apiURL.RawQuery = q.Encode() log.Debug().Msgf("Success: Building API Url.") - return url + return apiURL } // HTTPRequest is generic method for HTTP Requests to server func HTTPRequest(data HTTPRequestType) *http.Response { log.Debug().Msgf("Executing HTTPRequest.") client := &http.Client{} - url := buildAPIURL(data.Host, data.Endpoint, data.ThreeScaleToken) + apiURL := buildAPIURL(data.Host, data.Endpoint, data.ThreeScaleToken) payload, _ := json.Marshal(&data.Payload) - req, err := http.NewRequest(data.Method, url.String(), bytes.NewBuffer(payload)) + req, err := http.NewRequest(data.Method, apiURL.String(), bytes.NewBuffer(payload)) req.Header.Add("Content-Type", "application/json") req.Header.Add("uuid", data.UserID) @@ -64,12 +79,13 @@ func HTTPRequest(data HTTPRequestType) *http.Response { func HTTPRequestMultipart(data HTTPRequestType, w *multipart.Writer, buf *bytes.Buffer) *http.Response { log.Debug().Msgf("Executing HTTPRequestMultipart.") client := &http.Client{} - url := buildAPIURL(data.Host, data.Endpoint, data.ThreeScaleToken) - req, err := http.NewRequest(data.Method, url.String(), buf) + apiURL := buildAPIURL(data.Host, data.Endpoint, data.ThreeScaleToken) + req, err := http.NewRequest(data.Method, apiURL.String(), buf) if err != nil { log.Fatal().Err(err).Msgf("Unable to build request") } req.Header.Set("Content-Type", w.FormDataContentType()) + req.Header.Set("uuid", data.UserID) res, err := client.Do(req) if err != nil { log.Fatal().Err(err).Msgf("Unable to reach the server. hint: Check your Internet connection.") diff --git a/utils/constants.go b/utils/constants.go index bcebd05..65b7004 100644 --- a/utils/constants.go +++ b/utils/constants.go @@ -2,7 +2,8 @@ package utils // Flag Defaults const ( - Host string = "https://f8a-analytics-2445582058137.production.gw.apicast.io:443" - AuthToken string = "9e7da76708fe374d8c10fa752e72989f" - Debug bool = false + Host string = "https://f8a-analytics-2445582058137.production.gw.apicast.io" + AuthToken string = "9e7da76708fe374d8c10fa752e72989f" + Debug bool = false + ActualHost string = "https://recommender.api.openshift.io" )