diff --git a/pkg/guacrest/client/client.go b/pkg/guacrest/client/client.go index e67bc53470..b631ab6d22 100644 --- a/pkg/guacrest/client/client.go +++ b/pkg/guacrest/client/client.go @@ -94,8 +94,20 @@ type ClientInterface interface { // HealthCheck request HealthCheck(ctx context.Context, reqEditors ...RequestEditorFn) (*http.Response, error) - // RetrieveDependencies request - RetrieveDependencies(ctx context.Context, params *RetrieveDependenciesParams, reqEditors ...RequestEditorFn) (*http.Response, error) + // GetArtifactDeps request + GetArtifactDeps(ctx context.Context, digest string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetArtifactVulns request + GetArtifactVulns(ctx context.Context, digest string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetPackagePurls request + GetPackagePurls(ctx context.Context, purl string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetPackageDeps request + GetPackageDeps(ctx context.Context, purl string, reqEditors ...RequestEditorFn) (*http.Response, error) + + // GetPackageVulns request + GetPackageVulns(ctx context.Context, purl string, params *GetPackageVulnsParams, reqEditors ...RequestEditorFn) (*http.Response, error) } func (c *Client) AnalyzeDependencies(ctx context.Context, params *AnalyzeDependenciesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { @@ -122,8 +134,56 @@ func (c *Client) HealthCheck(ctx context.Context, reqEditors ...RequestEditorFn) return c.Client.Do(req) } -func (c *Client) RetrieveDependencies(ctx context.Context, params *RetrieveDependenciesParams, reqEditors ...RequestEditorFn) (*http.Response, error) { - req, err := NewRetrieveDependenciesRequest(c.Server, params) +func (c *Client) GetArtifactDeps(ctx context.Context, digest string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetArtifactDepsRequest(c.Server, digest) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetArtifactVulns(ctx context.Context, digest string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetArtifactVulnsRequest(c.Server, digest) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetPackagePurls(ctx context.Context, purl string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetPackagePurlsRequest(c.Server, purl) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetPackageDeps(ctx context.Context, purl string, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetPackageDepsRequest(c.Server, purl) + if err != nil { + return nil, err + } + req = req.WithContext(ctx) + if err := c.applyEditors(ctx, req, reqEditors); err != nil { + return nil, err + } + return c.Client.Do(req) +} + +func (c *Client) GetPackageVulns(ctx context.Context, purl string, params *GetPackageVulnsParams, reqEditors ...RequestEditorFn) (*http.Response, error) { + req, err := NewGetPackageVulnsRequest(c.Server, purl, params) if err != nil { return nil, err } @@ -222,16 +282,23 @@ func NewHealthCheckRequest(server string) (*http.Request, error) { return req, nil } -// NewRetrieveDependenciesRequest generates requests for RetrieveDependencies -func NewRetrieveDependenciesRequest(server string, params *RetrieveDependenciesParams) (*http.Request, error) { +// NewGetArtifactDepsRequest generates requests for GetArtifactDeps +func NewGetArtifactDepsRequest(server string, digest string) (*http.Request, error) { var err error + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "digest", runtime.ParamLocationPath, digest) + if err != nil { + return nil, err + } + serverURL, err := url.Parse(server) if err != nil { return nil, err } - operationPath := fmt.Sprintf("/query/dependencies") + operationPath := fmt.Sprintf("/v0/artifact/%s/dependencies", pathParam0) if operationPath[0] == '/' { operationPath = "." + operationPath } @@ -241,60 +308,148 @@ func NewRetrieveDependenciesRequest(server string, params *RetrieveDependenciesP return nil, err } - if params != nil { - queryValues := queryURL.Query() + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } - if params.PaginationSpec != nil { + return req, nil +} - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "paginationSpec", runtime.ParamLocationQuery, *params.PaginationSpec); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } +// NewGetArtifactVulnsRequest generates requests for GetArtifactVulns +func NewGetArtifactVulnsRequest(server string, digest string) (*http.Request, error) { + var err error - } + var pathParam0 string - if params.LinkCondition != nil { + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "digest", runtime.ParamLocationPath, digest) + if err != nil { + return nil, err + } - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "linkCondition", runtime.ParamLocationQuery, *params.LinkCondition); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } - } + operationPath := fmt.Sprintf("/v0/artifact/%s/vulns", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } - if params.Purl != nil { + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "purl", runtime.ParamLocationQuery, *params.Purl); err != nil { - return nil, err - } else if parsed, err := url.ParseQuery(queryFrag); err != nil { - return nil, err - } else { - for k, v := range parsed { - for _, v2 := range v { - queryValues.Add(k, v2) - } - } - } + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } - } + return req, nil +} + +// NewGetPackagePurlsRequest generates requests for GetPackagePurls +func NewGetPackagePurlsRequest(server string, purl string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "purl", runtime.ParamLocationPath, purl) + if err != nil { + return nil, err + } - if params.Digest != nil { + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v0/package/%s", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetPackageDepsRequest generates requests for GetPackageDeps +func NewGetPackageDepsRequest(server string, purl string) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "purl", runtime.ParamLocationPath, purl) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v0/package/%s/dependencies", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + req, err := http.NewRequest("GET", queryURL.String(), nil) + if err != nil { + return nil, err + } + + return req, nil +} + +// NewGetPackageVulnsRequest generates requests for GetPackageVulns +func NewGetPackageVulnsRequest(server string, purl string, params *GetPackageVulnsParams) (*http.Request, error) { + var err error + + var pathParam0 string + + pathParam0, err = runtime.StyleParamWithLocation("simple", false, "purl", runtime.ParamLocationPath, purl) + if err != nil { + return nil, err + } + + serverURL, err := url.Parse(server) + if err != nil { + return nil, err + } + + operationPath := fmt.Sprintf("/v0/package/%s/vulns", pathParam0) + if operationPath[0] == '/' { + operationPath = "." + operationPath + } + + queryURL, err := serverURL.Parse(operationPath) + if err != nil { + return nil, err + } + + if params != nil { + queryValues := queryURL.Query() + + if params.IncludeDependencies != nil { - if queryFrag, err := runtime.StyleParamWithLocation("form", true, "digest", runtime.ParamLocationQuery, *params.Digest); err != nil { + if queryFrag, err := runtime.StyleParamWithLocation("form", true, "includeDependencies", runtime.ParamLocationQuery, *params.IncludeDependencies); err != nil { return nil, err } else if parsed, err := url.ParseQuery(queryFrag); err != nil { return nil, err @@ -368,8 +523,20 @@ type ClientWithResponsesInterface interface { // HealthCheckWithResponse request HealthCheckWithResponse(ctx context.Context, reqEditors ...RequestEditorFn) (*HealthCheckResponse, error) - // RetrieveDependenciesWithResponse request - RetrieveDependenciesWithResponse(ctx context.Context, params *RetrieveDependenciesParams, reqEditors ...RequestEditorFn) (*RetrieveDependenciesResponse, error) + // GetArtifactDepsWithResponse request + GetArtifactDepsWithResponse(ctx context.Context, digest string, reqEditors ...RequestEditorFn) (*GetArtifactDepsResponse, error) + + // GetArtifactVulnsWithResponse request + GetArtifactVulnsWithResponse(ctx context.Context, digest string, reqEditors ...RequestEditorFn) (*GetArtifactVulnsResponse, error) + + // GetPackagePurlsWithResponse request + GetPackagePurlsWithResponse(ctx context.Context, purl string, reqEditors ...RequestEditorFn) (*GetPackagePurlsResponse, error) + + // GetPackageDepsWithResponse request + GetPackageDepsWithResponse(ctx context.Context, purl string, reqEditors ...RequestEditorFn) (*GetPackageDepsResponse, error) + + // GetPackageVulnsWithResponse request + GetPackageVulnsWithResponse(ctx context.Context, purl string, params *GetPackageVulnsParams, reqEditors ...RequestEditorFn) (*GetPackageVulnsResponse, error) } type AnalyzeDependenciesResponse struct { @@ -419,7 +586,7 @@ func (r HealthCheckResponse) StatusCode() int { return 0 } -type RetrieveDependenciesResponse struct { +type GetArtifactDepsResponse struct { Body []byte HTTPResponse *http.Response JSON200 *PurlList @@ -429,7 +596,32 @@ type RetrieveDependenciesResponse struct { } // Status returns HTTPResponse.Status -func (r RetrieveDependenciesResponse) Status() string { +func (r GetArtifactDepsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetArtifactDepsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetArtifactVulnsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *VulnerabilityList + JSON400 *BadRequest + JSON500 *InternalServerError + JSON502 *BadGateway +} + +// Status returns HTTPResponse.Status +func (r GetArtifactVulnsResponse) Status() string { if r.HTTPResponse != nil { return r.HTTPResponse.Status } @@ -437,7 +629,82 @@ func (r RetrieveDependenciesResponse) Status() string { } // StatusCode returns HTTPResponse.StatusCode -func (r RetrieveDependenciesResponse) StatusCode() int { +func (r GetArtifactVulnsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetPackagePurlsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *PurlList + JSON400 *BadRequest + JSON500 *InternalServerError + JSON502 *BadGateway +} + +// Status returns HTTPResponse.Status +func (r GetPackagePurlsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPackagePurlsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetPackageDepsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *PurlList + JSON400 *BadRequest + JSON500 *InternalServerError + JSON502 *BadGateway +} + +// Status returns HTTPResponse.Status +func (r GetPackageDepsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPackageDepsResponse) StatusCode() int { + if r.HTTPResponse != nil { + return r.HTTPResponse.StatusCode + } + return 0 +} + +type GetPackageVulnsResponse struct { + Body []byte + HTTPResponse *http.Response + JSON200 *VulnerabilityList + JSON400 *BadRequest + JSON500 *InternalServerError + JSON502 *BadGateway +} + +// Status returns HTTPResponse.Status +func (r GetPackageVulnsResponse) Status() string { + if r.HTTPResponse != nil { + return r.HTTPResponse.Status + } + return http.StatusText(0) +} + +// StatusCode returns HTTPResponse.StatusCode +func (r GetPackageVulnsResponse) StatusCode() int { if r.HTTPResponse != nil { return r.HTTPResponse.StatusCode } @@ -462,13 +729,49 @@ func (c *ClientWithResponses) HealthCheckWithResponse(ctx context.Context, reqEd return ParseHealthCheckResponse(rsp) } -// RetrieveDependenciesWithResponse request returning *RetrieveDependenciesResponse -func (c *ClientWithResponses) RetrieveDependenciesWithResponse(ctx context.Context, params *RetrieveDependenciesParams, reqEditors ...RequestEditorFn) (*RetrieveDependenciesResponse, error) { - rsp, err := c.RetrieveDependencies(ctx, params, reqEditors...) +// GetArtifactDepsWithResponse request returning *GetArtifactDepsResponse +func (c *ClientWithResponses) GetArtifactDepsWithResponse(ctx context.Context, digest string, reqEditors ...RequestEditorFn) (*GetArtifactDepsResponse, error) { + rsp, err := c.GetArtifactDeps(ctx, digest, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetArtifactDepsResponse(rsp) +} + +// GetArtifactVulnsWithResponse request returning *GetArtifactVulnsResponse +func (c *ClientWithResponses) GetArtifactVulnsWithResponse(ctx context.Context, digest string, reqEditors ...RequestEditorFn) (*GetArtifactVulnsResponse, error) { + rsp, err := c.GetArtifactVulns(ctx, digest, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetArtifactVulnsResponse(rsp) +} + +// GetPackagePurlsWithResponse request returning *GetPackagePurlsResponse +func (c *ClientWithResponses) GetPackagePurlsWithResponse(ctx context.Context, purl string, reqEditors ...RequestEditorFn) (*GetPackagePurlsResponse, error) { + rsp, err := c.GetPackagePurls(ctx, purl, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetPackagePurlsResponse(rsp) +} + +// GetPackageDepsWithResponse request returning *GetPackageDepsResponse +func (c *ClientWithResponses) GetPackageDepsWithResponse(ctx context.Context, purl string, reqEditors ...RequestEditorFn) (*GetPackageDepsResponse, error) { + rsp, err := c.GetPackageDeps(ctx, purl, reqEditors...) if err != nil { return nil, err } - return ParseRetrieveDependenciesResponse(rsp) + return ParseGetPackageDepsResponse(rsp) +} + +// GetPackageVulnsWithResponse request returning *GetPackageVulnsResponse +func (c *ClientWithResponses) GetPackageVulnsWithResponse(ctx context.Context, purl string, params *GetPackageVulnsParams, reqEditors ...RequestEditorFn) (*GetPackageVulnsResponse, error) { + rsp, err := c.GetPackageVulns(ctx, purl, params, reqEditors...) + if err != nil { + return nil, err + } + return ParseGetPackageVulnsResponse(rsp) } // ParseAnalyzeDependenciesResponse parses an HTTP response from a AnalyzeDependenciesWithResponse call @@ -544,15 +847,15 @@ func ParseHealthCheckResponse(rsp *http.Response) (*HealthCheckResponse, error) return response, nil } -// ParseRetrieveDependenciesResponse parses an HTTP response from a RetrieveDependenciesWithResponse call -func ParseRetrieveDependenciesResponse(rsp *http.Response) (*RetrieveDependenciesResponse, error) { +// ParseGetArtifactDepsResponse parses an HTTP response from a GetArtifactDepsWithResponse call +func ParseGetArtifactDepsResponse(rsp *http.Response) (*GetArtifactDepsResponse, error) { bodyBytes, err := io.ReadAll(rsp.Body) defer func() { _ = rsp.Body.Close() }() if err != nil { return nil, err } - response := &RetrieveDependenciesResponse{ + response := &GetArtifactDepsResponse{ Body: bodyBytes, HTTPResponse: rsp, } @@ -590,3 +893,191 @@ func ParseRetrieveDependenciesResponse(rsp *http.Response) (*RetrieveDependencie return response, nil } + +// ParseGetArtifactVulnsResponse parses an HTTP response from a GetArtifactVulnsWithResponse call +func ParseGetArtifactVulnsResponse(rsp *http.Response) (*GetArtifactVulnsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetArtifactVulnsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest VulnerabilityList + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 502: + var dest BadGateway + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON502 = &dest + + } + + return response, nil +} + +// ParseGetPackagePurlsResponse parses an HTTP response from a GetPackagePurlsWithResponse call +func ParseGetPackagePurlsResponse(rsp *http.Response) (*GetPackagePurlsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPackagePurlsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PurlList + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 502: + var dest BadGateway + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON502 = &dest + + } + + return response, nil +} + +// ParseGetPackageDepsResponse parses an HTTP response from a GetPackageDepsWithResponse call +func ParseGetPackageDepsResponse(rsp *http.Response) (*GetPackageDepsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPackageDepsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest PurlList + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 502: + var dest BadGateway + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON502 = &dest + + } + + return response, nil +} + +// ParseGetPackageVulnsResponse parses an HTTP response from a GetPackageVulnsWithResponse call +func ParseGetPackageVulnsResponse(rsp *http.Response) (*GetPackageVulnsResponse, error) { + bodyBytes, err := io.ReadAll(rsp.Body) + defer func() { _ = rsp.Body.Close() }() + if err != nil { + return nil, err + } + + response := &GetPackageVulnsResponse{ + Body: bodyBytes, + HTTPResponse: rsp, + } + + switch { + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 200: + var dest VulnerabilityList + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON200 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 400: + var dest BadRequest + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON400 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 500: + var dest InternalServerError + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON500 = &dest + + case strings.Contains(rsp.Header.Get("Content-Type"), "json") && rsp.StatusCode == 502: + var dest BadGateway + if err := json.Unmarshal(bodyBytes, &dest); err != nil { + return nil, err + } + response.JSON502 = &dest + + } + + return response, nil +} diff --git a/pkg/guacrest/client/models.go b/pkg/guacrest/client/models.go index 99a997e9f5..3d042c8905 100644 --- a/pkg/guacrest/client/models.go +++ b/pkg/guacrest/client/models.go @@ -3,18 +3,16 @@ // Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. package client +import ( + "time" +) + // Defines values for AnalyzeDependenciesParamsSort. const ( Frequency AnalyzeDependenciesParamsSort = "frequency" Scorecard AnalyzeDependenciesParamsSort = "scorecard" ) -// Defines values for RetrieveDependenciesParamsLinkCondition. -const ( - Digest RetrieveDependenciesParamsLinkCondition = "digest" - Name RetrieveDependenciesParamsLinkCondition = "name" -) - // Error defines model for Error. type Error struct { Message string `json:"Message"` @@ -35,6 +33,32 @@ type PaginationInfo struct { // Purl defines model for Purl. type Purl = string +// ScanMetadata defines model for ScanMetadata. +type ScanMetadata struct { + Collector *string `json:"collector,omitempty"` + DbUri *string `json:"dbUri,omitempty"` + DbVersion *string `json:"dbVersion,omitempty"` + Origin *string `json:"origin,omitempty"` + ScannerUri *string `json:"scannerUri,omitempty"` + ScannerVersion *string `json:"scannerVersion,omitempty"` + TimeScanned *time.Time `json:"timeScanned,omitempty"` +} + +// Vulnerability defines model for Vulnerability. +type Vulnerability struct { + Metadata ScanMetadata `json:"metadata"` + Package string `json:"package"` + Vulnerability VulnerabilityDetails `json:"vulnerability"` +} + +// VulnerabilityDetails defines model for VulnerabilityDetails. +type VulnerabilityDetails struct { + Type *string `json:"type,omitempty"` + + // VulnerabilityIDs A list of vulnerability identifiers. These can be CVE IDs or other formats used to identify vulnerabilities. + VulnerabilityIDs []string `json:"vulnerabilityIDs"` +} + // PaginationSpec defines model for PaginationSpec. type PaginationSpec struct { Cursor *string `json:"Cursor,omitempty"` @@ -60,6 +84,9 @@ type PurlList struct { PurlList []Purl `json:"PurlList"` } +// VulnerabilityList defines model for VulnerabilityList. +type VulnerabilityList = []Vulnerability + // AnalyzeDependenciesParams defines parameters for AnalyzeDependencies. type AnalyzeDependenciesParams struct { // PaginationSpec The pagination configuration for the query. @@ -76,22 +103,8 @@ type AnalyzeDependenciesParams struct { // AnalyzeDependenciesParamsSort defines parameters for AnalyzeDependencies. type AnalyzeDependenciesParamsSort string -// RetrieveDependenciesParams defines parameters for RetrieveDependencies. -type RetrieveDependenciesParams struct { - // PaginationSpec The pagination configuration for the query. - // * 'PageSize' specifies the number of results returned - // * 'Cursor' is returned by previous calls and specifies what page to return - PaginationSpec *PaginationSpec `form:"paginationSpec,omitempty" json:"paginationSpec,omitempty"` - - // LinkCondition Whether links between nouns must be made by digest or if they can be made just by name (i.e. purl). Specify 'name' to allow using SBOMs that don't provide the digest of the subject. The default is 'digest'. To search by purl, 'name' must be specified. - LinkCondition *RetrieveDependenciesParamsLinkCondition `form:"linkCondition,omitempty" json:"linkCondition,omitempty"` - - // Purl The purl of the dependent package. - Purl *string `form:"purl,omitempty" json:"purl,omitempty"` - - // Digest The digest of the dependent package. - Digest *string `form:"digest,omitempty" json:"digest,omitempty"` +// GetPackageVulnsParams defines parameters for GetPackageVulns. +type GetPackageVulnsParams struct { + // IncludeDependencies A flag to include vulnerabilities of the dependencies. If true, the response will include vulnerabilities for the purl and its dependencies instead of the vulnerabilities of just the purl. + IncludeDependencies *bool `form:"includeDependencies,omitempty" json:"includeDependencies,omitempty"` } - -// RetrieveDependenciesParamsLinkCondition defines parameters for RetrieveDependencies. -type RetrieveDependenciesParamsLinkCondition string diff --git a/pkg/guacrest/generated/models.go b/pkg/guacrest/generated/models.go index e522ae925a..62a9ac0534 100644 --- a/pkg/guacrest/generated/models.go +++ b/pkg/guacrest/generated/models.go @@ -3,18 +3,16 @@ // Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. package generated +import ( + "time" +) + // Defines values for AnalyzeDependenciesParamsSort. const ( Frequency AnalyzeDependenciesParamsSort = "frequency" Scorecard AnalyzeDependenciesParamsSort = "scorecard" ) -// Defines values for RetrieveDependenciesParamsLinkCondition. -const ( - Digest RetrieveDependenciesParamsLinkCondition = "digest" - Name RetrieveDependenciesParamsLinkCondition = "name" -) - // Error defines model for Error. type Error struct { Message string `json:"Message"` @@ -35,6 +33,32 @@ type PaginationInfo struct { // Purl defines model for Purl. type Purl = string +// ScanMetadata defines model for ScanMetadata. +type ScanMetadata struct { + Collector *string `json:"collector,omitempty"` + DbUri *string `json:"dbUri,omitempty"` + DbVersion *string `json:"dbVersion,omitempty"` + Origin *string `json:"origin,omitempty"` + ScannerUri *string `json:"scannerUri,omitempty"` + ScannerVersion *string `json:"scannerVersion,omitempty"` + TimeScanned *time.Time `json:"timeScanned,omitempty"` +} + +// Vulnerability defines model for Vulnerability. +type Vulnerability struct { + Metadata ScanMetadata `json:"metadata"` + Package string `json:"package"` + Vulnerability VulnerabilityDetails `json:"vulnerability"` +} + +// VulnerabilityDetails defines model for VulnerabilityDetails. +type VulnerabilityDetails struct { + Type *string `json:"type,omitempty"` + + // VulnerabilityIDs A list of vulnerability identifiers. These can be CVE IDs or other formats used to identify vulnerabilities. + VulnerabilityIDs []string `json:"vulnerabilityIDs"` +} + // PaginationSpec defines model for PaginationSpec. type PaginationSpec struct { Cursor *string `json:"Cursor,omitempty"` @@ -60,6 +84,9 @@ type PurlList struct { PurlList []Purl `json:"PurlList"` } +// VulnerabilityList defines model for VulnerabilityList. +type VulnerabilityList = []Vulnerability + // AnalyzeDependenciesParams defines parameters for AnalyzeDependencies. type AnalyzeDependenciesParams struct { // PaginationSpec The pagination configuration for the query. @@ -76,22 +103,8 @@ type AnalyzeDependenciesParams struct { // AnalyzeDependenciesParamsSort defines parameters for AnalyzeDependencies. type AnalyzeDependenciesParamsSort string -// RetrieveDependenciesParams defines parameters for RetrieveDependencies. -type RetrieveDependenciesParams struct { - // PaginationSpec The pagination configuration for the query. - // * 'PageSize' specifies the number of results returned - // * 'Cursor' is returned by previous calls and specifies what page to return - PaginationSpec *PaginationSpec `form:"paginationSpec,omitempty" json:"paginationSpec,omitempty"` - - // LinkCondition Whether links between nouns must be made by digest or if they can be made just by name (i.e. purl). Specify 'name' to allow using SBOMs that don't provide the digest of the subject. The default is 'digest'. To search by purl, 'name' must be specified. - LinkCondition *RetrieveDependenciesParamsLinkCondition `form:"linkCondition,omitempty" json:"linkCondition,omitempty"` - - // Purl The purl of the dependent package. - Purl *string `form:"purl,omitempty" json:"purl,omitempty"` - - // Digest The digest of the dependent package. - Digest *string `form:"digest,omitempty" json:"digest,omitempty"` +// GetPackageVulnsParams defines parameters for GetPackageVulns. +type GetPackageVulnsParams struct { + // IncludeDependencies A flag to include vulnerabilities of the dependencies. If true, the response will include vulnerabilities for the purl and its dependencies instead of the vulnerabilities of just the purl. + IncludeDependencies *bool `form:"includeDependencies,omitempty" json:"includeDependencies,omitempty"` } - -// RetrieveDependenciesParamsLinkCondition defines parameters for RetrieveDependencies. -type RetrieveDependenciesParamsLinkCondition string diff --git a/pkg/guacrest/generated/server.go b/pkg/guacrest/generated/server.go index 57716db38b..cf1bd4086d 100644 --- a/pkg/guacrest/generated/server.go +++ b/pkg/guacrest/generated/server.go @@ -22,9 +22,21 @@ type ServerInterface interface { // Health check the server // (GET /healthz) HealthCheck(w http.ResponseWriter, r *http.Request) - // Retrieve transitive dependencies - // (GET /query/dependencies) - RetrieveDependencies(w http.ResponseWriter, r *http.Request, params RetrieveDependenciesParams) + // Get dependencies for a specific digest + // (GET /v0/artifact/{digest}/dependencies) + GetArtifactDeps(w http.ResponseWriter, r *http.Request, digest string) + // Get vulnerabilities for a specific digest + // (GET /v0/artifact/{digest}/vulns) + GetArtifactVulns(w http.ResponseWriter, r *http.Request, digest string) + // Get all purls related to the given purl + // (GET /v0/package/{purl}) + GetPackagePurls(w http.ResponseWriter, r *http.Request, purl string) + // Get dependencies for a specific Package URL (purl) + // (GET /v0/package/{purl}/dependencies) + GetPackageDeps(w http.ResponseWriter, r *http.Request, purl string) + // Get vulnerabilities for a Package URL (purl) + // (GET /v0/package/{purl}/vulns) + GetPackageVulns(w http.ResponseWriter, r *http.Request, purl string, params GetPackageVulnsParams) } // Unimplemented server implementation that returns http.StatusNotImplemented for each endpoint. @@ -43,9 +55,33 @@ func (_ Unimplemented) HealthCheck(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNotImplemented) } -// Retrieve transitive dependencies -// (GET /query/dependencies) -func (_ Unimplemented) RetrieveDependencies(w http.ResponseWriter, r *http.Request, params RetrieveDependenciesParams) { +// Get dependencies for a specific digest +// (GET /v0/artifact/{digest}/dependencies) +func (_ Unimplemented) GetArtifactDeps(w http.ResponseWriter, r *http.Request, digest string) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Get vulnerabilities for a specific digest +// (GET /v0/artifact/{digest}/vulns) +func (_ Unimplemented) GetArtifactVulns(w http.ResponseWriter, r *http.Request, digest string) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Get all purls related to the given purl +// (GET /v0/package/{purl}) +func (_ Unimplemented) GetPackagePurls(w http.ResponseWriter, r *http.Request, purl string) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Get dependencies for a specific Package URL (purl) +// (GET /v0/package/{purl}/dependencies) +func (_ Unimplemented) GetPackageDeps(w http.ResponseWriter, r *http.Request, purl string) { + w.WriteHeader(http.StatusNotImplemented) +} + +// Get vulnerabilities for a Package URL (purl) +// (GET /v0/package/{purl}/vulns) +func (_ Unimplemented) GetPackageVulns(w http.ResponseWriter, r *http.Request, purl string, params GetPackageVulnsParams) { w.WriteHeader(http.StatusNotImplemented) } @@ -114,48 +150,133 @@ func (siw *ServerInterfaceWrapper) HealthCheck(w http.ResponseWriter, r *http.Re handler.ServeHTTP(w, r) } -// RetrieveDependencies operation middleware -func (siw *ServerInterfaceWrapper) RetrieveDependencies(w http.ResponseWriter, r *http.Request) { +// GetArtifactDeps operation middleware +func (siw *ServerInterfaceWrapper) GetArtifactDeps(w http.ResponseWriter, r *http.Request) { var err error - // Parameter object where we will unmarshal all parameters from the context - var params RetrieveDependenciesParams + // ------------- Path parameter "digest" ------------- + var digest string - // ------------- Optional query parameter "paginationSpec" ------------- + err = runtime.BindStyledParameterWithOptions("simple", "digest", chi.URLParam(r, "digest"), &digest, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "digest", Err: err}) + return + } - err = runtime.BindQueryParameter("form", true, false, "paginationSpec", r.URL.Query(), ¶ms.PaginationSpec) + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetArtifactDeps(w, r, digest) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetArtifactVulns operation middleware +func (siw *ServerInterfaceWrapper) GetArtifactVulns(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "digest" ------------- + var digest string + + err = runtime.BindStyledParameterWithOptions("simple", "digest", chi.URLParam(r, "digest"), &digest, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "paginationSpec", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "digest", Err: err}) return } - // ------------- Optional query parameter "linkCondition" ------------- + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetArtifactVulns(w, r, digest) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetPackagePurls operation middleware +func (siw *ServerInterfaceWrapper) GetPackagePurls(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "purl" ------------- + var purl string - err = runtime.BindQueryParameter("form", true, false, "linkCondition", r.URL.Query(), ¶ms.LinkCondition) + err = runtime.BindStyledParameterWithOptions("simple", "purl", chi.URLParam(r, "purl"), &purl, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "linkCondition", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "purl", Err: err}) return } - // ------------- Optional query parameter "purl" ------------- + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetPackagePurls(w, r, purl) + })) + + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetPackageDeps operation middleware +func (siw *ServerInterfaceWrapper) GetPackageDeps(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "purl" ------------- + var purl string - err = runtime.BindQueryParameter("form", true, false, "purl", r.URL.Query(), ¶ms.Purl) + err = runtime.BindStyledParameterWithOptions("simple", "purl", chi.URLParam(r, "purl"), &purl, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "purl", Err: err}) return } - // ------------- Optional query parameter "digest" ------------- + handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + siw.Handler.GetPackageDeps(w, r, purl) + })) - err = runtime.BindQueryParameter("form", true, false, "digest", r.URL.Query(), ¶ms.Digest) + for _, middleware := range siw.HandlerMiddlewares { + handler = middleware(handler) + } + + handler.ServeHTTP(w, r) +} + +// GetPackageVulns operation middleware +func (siw *ServerInterfaceWrapper) GetPackageVulns(w http.ResponseWriter, r *http.Request) { + + var err error + + // ------------- Path parameter "purl" ------------- + var purl string + + err = runtime.BindStyledParameterWithOptions("simple", "purl", chi.URLParam(r, "purl"), &purl, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationPath, Explode: false, Required: true}) if err != nil { - siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "digest", Err: err}) + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "purl", Err: err}) + return + } + + // Parameter object where we will unmarshal all parameters from the context + var params GetPackageVulnsParams + + // ------------- Optional query parameter "includeDependencies" ------------- + + err = runtime.BindQueryParameter("form", true, false, "includeDependencies", r.URL.Query(), ¶ms.IncludeDependencies) + if err != nil { + siw.ErrorHandlerFunc(w, r, &InvalidParamFormatError{ParamName: "includeDependencies", Err: err}) return } handler := http.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { - siw.Handler.RetrieveDependencies(w, r, params) + siw.Handler.GetPackageVulns(w, r, purl, params) })) for _, middleware := range siw.HandlerMiddlewares { @@ -285,7 +406,19 @@ func HandlerWithOptions(si ServerInterface, options ChiServerOptions) http.Handl r.Get(options.BaseURL+"/healthz", wrapper.HealthCheck) }) r.Group(func(r chi.Router) { - r.Get(options.BaseURL+"/query/dependencies", wrapper.RetrieveDependencies) + r.Get(options.BaseURL+"/v0/artifact/{digest}/dependencies", wrapper.GetArtifactDeps) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/v0/artifact/{digest}/vulns", wrapper.GetArtifactVulns) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/v0/package/{purl}", wrapper.GetPackagePurls) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/v0/package/{purl}/dependencies", wrapper.GetPackageDeps) + }) + r.Group(func(r chi.Router) { + r.Get(options.BaseURL+"/v0/package/{purl}/vulns", wrapper.GetPackageVulns) }) return r @@ -305,6 +438,8 @@ type PurlListJSONResponse struct { PurlList []Purl `json:"PurlList"` } +type VulnerabilityListJSONResponse []Vulnerability + type AnalyzeDependenciesRequestObject struct { Params AnalyzeDependenciesParams } @@ -367,46 +502,231 @@ func (response HealthCheck200JSONResponse) VisitHealthCheckResponse(w http.Respo return json.NewEncoder(w).Encode(response) } -type RetrieveDependenciesRequestObject struct { - Params RetrieveDependenciesParams +type GetArtifactDepsRequestObject struct { + Digest string `json:"digest"` } -type RetrieveDependenciesResponseObject interface { - VisitRetrieveDependenciesResponse(w http.ResponseWriter) error +type GetArtifactDepsResponseObject interface { + VisitGetArtifactDepsResponse(w http.ResponseWriter) error } -type RetrieveDependencies200JSONResponse struct{ PurlListJSONResponse } +type GetArtifactDeps200JSONResponse struct{ PurlListJSONResponse } -func (response RetrieveDependencies200JSONResponse) VisitRetrieveDependenciesResponse(w http.ResponseWriter) error { +func (response GetArtifactDeps200JSONResponse) VisitGetArtifactDepsResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(200) return json.NewEncoder(w).Encode(response) } -type RetrieveDependencies400JSONResponse struct{ BadRequestJSONResponse } +type GetArtifactDeps400JSONResponse struct{ BadRequestJSONResponse } -func (response RetrieveDependencies400JSONResponse) VisitRetrieveDependenciesResponse(w http.ResponseWriter) error { +func (response GetArtifactDeps400JSONResponse) VisitGetArtifactDepsResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(400) return json.NewEncoder(w).Encode(response) } -type RetrieveDependencies500JSONResponse struct { +type GetArtifactDeps500JSONResponse struct { InternalServerErrorJSONResponse } -func (response RetrieveDependencies500JSONResponse) VisitRetrieveDependenciesResponse(w http.ResponseWriter) error { +func (response GetArtifactDeps500JSONResponse) VisitGetArtifactDepsResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(500) return json.NewEncoder(w).Encode(response) } -type RetrieveDependencies502JSONResponse struct{ BadGatewayJSONResponse } +type GetArtifactDeps502JSONResponse struct{ BadGatewayJSONResponse } -func (response RetrieveDependencies502JSONResponse) VisitRetrieveDependenciesResponse(w http.ResponseWriter) error { +func (response GetArtifactDeps502JSONResponse) VisitGetArtifactDepsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(502) + + return json.NewEncoder(w).Encode(response) +} + +type GetArtifactVulnsRequestObject struct { + Digest string `json:"digest"` +} + +type GetArtifactVulnsResponseObject interface { + VisitGetArtifactVulnsResponse(w http.ResponseWriter) error +} + +type GetArtifactVulns200JSONResponse struct{ VulnerabilityListJSONResponse } + +func (response GetArtifactVulns200JSONResponse) VisitGetArtifactVulnsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetArtifactVulns400JSONResponse struct{ BadRequestJSONResponse } + +func (response GetArtifactVulns400JSONResponse) VisitGetArtifactVulnsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetArtifactVulns500JSONResponse struct { + InternalServerErrorJSONResponse +} + +func (response GetArtifactVulns500JSONResponse) VisitGetArtifactVulnsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type GetArtifactVulns502JSONResponse struct{ BadGatewayJSONResponse } + +func (response GetArtifactVulns502JSONResponse) VisitGetArtifactVulnsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(502) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackagePurlsRequestObject struct { + Purl string `json:"purl"` +} + +type GetPackagePurlsResponseObject interface { + VisitGetPackagePurlsResponse(w http.ResponseWriter) error +} + +type GetPackagePurls200JSONResponse struct{ PurlListJSONResponse } + +func (response GetPackagePurls200JSONResponse) VisitGetPackagePurlsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackagePurls400JSONResponse struct{ BadRequestJSONResponse } + +func (response GetPackagePurls400JSONResponse) VisitGetPackagePurlsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackagePurls500JSONResponse struct { + InternalServerErrorJSONResponse +} + +func (response GetPackagePurls500JSONResponse) VisitGetPackagePurlsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackagePurls502JSONResponse struct{ BadGatewayJSONResponse } + +func (response GetPackagePurls502JSONResponse) VisitGetPackagePurlsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(502) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackageDepsRequestObject struct { + Purl string `json:"purl"` +} + +type GetPackageDepsResponseObject interface { + VisitGetPackageDepsResponse(w http.ResponseWriter) error +} + +type GetPackageDeps200JSONResponse struct{ PurlListJSONResponse } + +func (response GetPackageDeps200JSONResponse) VisitGetPackageDepsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackageDeps400JSONResponse struct{ BadRequestJSONResponse } + +func (response GetPackageDeps400JSONResponse) VisitGetPackageDepsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackageDeps500JSONResponse struct { + InternalServerErrorJSONResponse +} + +func (response GetPackageDeps500JSONResponse) VisitGetPackageDepsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackageDeps502JSONResponse struct{ BadGatewayJSONResponse } + +func (response GetPackageDeps502JSONResponse) VisitGetPackageDepsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(502) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackageVulnsRequestObject struct { + Purl string `json:"purl"` + Params GetPackageVulnsParams +} + +type GetPackageVulnsResponseObject interface { + VisitGetPackageVulnsResponse(w http.ResponseWriter) error +} + +type GetPackageVulns200JSONResponse struct{ VulnerabilityListJSONResponse } + +func (response GetPackageVulns200JSONResponse) VisitGetPackageVulnsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackageVulns400JSONResponse struct{ BadRequestJSONResponse } + +func (response GetPackageVulns400JSONResponse) VisitGetPackageVulnsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(400) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackageVulns500JSONResponse struct { + InternalServerErrorJSONResponse +} + +func (response GetPackageVulns500JSONResponse) VisitGetPackageVulnsResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(500) + + return json.NewEncoder(w).Encode(response) +} + +type GetPackageVulns502JSONResponse struct{ BadGatewayJSONResponse } + +func (response GetPackageVulns502JSONResponse) VisitGetPackageVulnsResponse(w http.ResponseWriter) error { w.Header().Set("Content-Type", "application/json") w.WriteHeader(502) @@ -421,9 +741,21 @@ type StrictServerInterface interface { // Health check the server // (GET /healthz) HealthCheck(ctx context.Context, request HealthCheckRequestObject) (HealthCheckResponseObject, error) - // Retrieve transitive dependencies - // (GET /query/dependencies) - RetrieveDependencies(ctx context.Context, request RetrieveDependenciesRequestObject) (RetrieveDependenciesResponseObject, error) + // Get dependencies for a specific digest + // (GET /v0/artifact/{digest}/dependencies) + GetArtifactDeps(ctx context.Context, request GetArtifactDepsRequestObject) (GetArtifactDepsResponseObject, error) + // Get vulnerabilities for a specific digest + // (GET /v0/artifact/{digest}/vulns) + GetArtifactVulns(ctx context.Context, request GetArtifactVulnsRequestObject) (GetArtifactVulnsResponseObject, error) + // Get all purls related to the given purl + // (GET /v0/package/{purl}) + GetPackagePurls(ctx context.Context, request GetPackagePurlsRequestObject) (GetPackagePurlsResponseObject, error) + // Get dependencies for a specific Package URL (purl) + // (GET /v0/package/{purl}/dependencies) + GetPackageDeps(ctx context.Context, request GetPackageDepsRequestObject) (GetPackageDepsResponseObject, error) + // Get vulnerabilities for a Package URL (purl) + // (GET /v0/package/{purl}/vulns) + GetPackageVulns(ctx context.Context, request GetPackageVulnsRequestObject) (GetPackageVulnsResponseObject, error) } type StrictHandlerFunc = strictnethttp.StrictHTTPHandlerFunc @@ -505,25 +837,130 @@ func (sh *strictHandler) HealthCheck(w http.ResponseWriter, r *http.Request) { } } -// RetrieveDependencies operation middleware -func (sh *strictHandler) RetrieveDependencies(w http.ResponseWriter, r *http.Request, params RetrieveDependenciesParams) { - var request RetrieveDependenciesRequestObject +// GetArtifactDeps operation middleware +func (sh *strictHandler) GetArtifactDeps(w http.ResponseWriter, r *http.Request, digest string) { + var request GetArtifactDepsRequestObject + + request.Digest = digest + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetArtifactDeps(ctx, request.(GetArtifactDepsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetArtifactDeps") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetArtifactDepsResponseObject); ok { + if err := validResponse.VisitGetArtifactDepsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetArtifactVulns operation middleware +func (sh *strictHandler) GetArtifactVulns(w http.ResponseWriter, r *http.Request, digest string) { + var request GetArtifactVulnsRequestObject + + request.Digest = digest + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetArtifactVulns(ctx, request.(GetArtifactVulnsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetArtifactVulns") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetArtifactVulnsResponseObject); ok { + if err := validResponse.VisitGetArtifactVulnsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetPackagePurls operation middleware +func (sh *strictHandler) GetPackagePurls(w http.ResponseWriter, r *http.Request, purl string) { + var request GetPackagePurlsRequestObject + + request.Purl = purl + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetPackagePurls(ctx, request.(GetPackagePurlsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetPackagePurls") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetPackagePurlsResponseObject); ok { + if err := validResponse.VisitGetPackagePurlsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetPackageDeps operation middleware +func (sh *strictHandler) GetPackageDeps(w http.ResponseWriter, r *http.Request, purl string) { + var request GetPackageDepsRequestObject + + request.Purl = purl + + handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { + return sh.ssi.GetPackageDeps(ctx, request.(GetPackageDepsRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "GetPackageDeps") + } + + response, err := handler(r.Context(), w, r, request) + + if err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } else if validResponse, ok := response.(GetPackageDepsResponseObject); ok { + if err := validResponse.VisitGetPackageDepsResponse(w); err != nil { + sh.options.ResponseErrorHandlerFunc(w, r, err) + } + } else if response != nil { + sh.options.ResponseErrorHandlerFunc(w, r, fmt.Errorf("unexpected response type: %T", response)) + } +} + +// GetPackageVulns operation middleware +func (sh *strictHandler) GetPackageVulns(w http.ResponseWriter, r *http.Request, purl string, params GetPackageVulnsParams) { + var request GetPackageVulnsRequestObject + request.Purl = purl request.Params = params handler := func(ctx context.Context, w http.ResponseWriter, r *http.Request, request interface{}) (interface{}, error) { - return sh.ssi.RetrieveDependencies(ctx, request.(RetrieveDependenciesRequestObject)) + return sh.ssi.GetPackageVulns(ctx, request.(GetPackageVulnsRequestObject)) } for _, middleware := range sh.middlewares { - handler = middleware(handler, "RetrieveDependencies") + handler = middleware(handler, "GetPackageVulns") } response, err := handler(r.Context(), w, r, request) if err != nil { sh.options.ResponseErrorHandlerFunc(w, r, err) - } else if validResponse, ok := response.(RetrieveDependenciesResponseObject); ok { - if err := validResponse.VisitRetrieveDependenciesResponse(w); err != nil { + } else if validResponse, ok := response.(GetPackageVulnsResponseObject); ok { + if err := validResponse.VisitGetPackageVulnsResponse(w); err != nil { sh.options.ResponseErrorHandlerFunc(w, r, err) } } else if response != nil { diff --git a/pkg/guacrest/generated/spec.go b/pkg/guacrest/generated/spec.go index ea98eb045d..39b78f542c 100644 --- a/pkg/guacrest/generated/spec.go +++ b/pkg/guacrest/generated/spec.go @@ -18,27 +18,34 @@ import ( // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/9RX32/bNhD+Vw7cAG+DZgfd9pK3Jm3XAP0R1AH20PaBIc8WG4pUjqQ9tfD/Phwl2ZIr", - "r+mAPOxNEnm87767+476IpSvau/QxSDOv4hakqwwIuW3a7k2Tkbj3bJGxV80BkWm5k/iXNyUCPV+Dyjv", - "VmadqH1beYJYItwnpGb+wQH8ArNrucal+YwzCDUqszIY8iaXqlsk8CsgDMnGAIQxkUPdGV4mCp5mYA4r", - "cNtATbgxPgVQ0toA0unBwdtSRsaHEH1n9cGJQhjGnmGJQjhZoTgX9TjUQgRVYiUzJ+RrpGgwc9IC4afY", - "1GwZIhm3FrtC9MENFo2LuEYSu13Rf/K3n1BFseNPhKH2LrQnX0j9p4y4lQ2/Ke8iusiPsq6tURnc4lNg", - "5r8M4P1IuBLn4ofFIZOLdjUsnhN5al19nbmAtEECdMonF5FQg3SAbMKpdKiicWvmjjOkZZRwK9UdOs3B", - "Xkj9Du8Thvj4aC+kBmqdFRCSKkEGWJGvwLiNtEaDJ6hMCIx3UMK7QlxxZE7aZQ629fDoeHun0HqFbiNX", - "iLqTa3wjK3xlvpM5E7EK34I0cCAOJSeJZDMF9ClYEyK3Xd0aArdDgK2JJWfdEGis0Wl0EXKZZFKvE9nv", - "xj9uo4O0XLmV/3ZYo91HEB7GTCI7QQn34H0yhFqcvz9GNXDzcbJ/T7KZyIZ8euee0e2Lb8zEawxBrnFC", - "UY7A9Ru/hjIqrK89POtTeMkZnFKnQvSW36bwCFU2LI59TGM8TvmYvkvvojSuHQgqy2wn3GRwg1B5yuMG", - "wxyuVryLECQhOJ/XCoA3+HdsBRq2xlq4RXDGzrPqjzk57JyU8hsfpT1J124qOiZnIoc7njccrkvWFsLX", - "6GRtxLn4bX42P2NcMpYZ0kI6aZtgwqLvOdWBXWOGwfhb/jRXG+/+jM+Ge4vRAH8/nc7DlsXRgN8VUxM+", - "eIrgSbfzOeaZn6stdLN5lbXZqWYGv8LNYH2vI1CadYkhDub8Xlf6U4LyhEqSPn2K9Vs+5G2Nbrl8AXuL", - "9unkbOcAxLBqIyUcTnh0qeJa3geS5393+KCW90n9eDS6n5ydnWqe/b7FsfjvCvH7Q+wGk3ZXiD8eYjI1", - "9bLtkwe5668hWcBSVUlqeKpxtsyqyamofIhgqtpTlC7CqGLZbFGitLH8fLJ8X+b1yxLVnZhm88GTZaLl", - "juexZuvurtnde0yAFuNxnC0yUAxtYNCGlUvrVIOOvb4wTmf7SNIFE80GRzz13WRcneI8l/xLGZYXb1/n", - "eyw/v1o+5Stuj57FLgW+p7WR7E9rgNBmekJp6mzO61fhrVKJCJ3C/BGu79bP75O0k6dGDyvGrLwLhtud", - "G2YjLQ//vhlbLR2n8l2n0I8rRX+VyJIP1ri7ALcYt4gOnE8uQJVCZLGvpEb+LdBmzULhCUzmuAFQ0u13", - "fMrbm3zZgZ/MHOd5Yv88h2X+eWhgxkszZkRa67eQ8tWSc8PMywjau1mEmvzGaGyT0flskxpSngxtWjWu", - "ZLKRSw5m7b7ZHG48BJSkyvwjk8gWvds+nP5XRs9PahuzcemdNpmlKVFr/fUWE2I2KfqMp4/lcAXsqmB+", - "6ieKZ2DxL5056WpM3IOd7eM67e6/yXR/5fu/6XPfhqf0hpVx908AAAD//0J3mDTxDwAA", + "H4sIAAAAAAAC/+xZW28buRX+KwdsAbfFRFKz7YueurF3UwPZrRE7ftkE2CPyzIgxh5yQHDmKof9ekDMj", + "zVWWgTpAunmzhjzkuXzfudAPjJu8MJq0d2z5wAq0mJMnG39dYSY1emn0dUE8fBHkuJVF+MSW7GZNUOz3", + "ADc6lVlpq1+pseDXBJ9KstvZew3wNzi7woyu5Rc6A1cQl6kkFzfpMl+RBZOCJVcq78CSL60mUQuel9YZ", + "ewbysAKrLRSWNtKUDjgq5QC1aB18v0Yf9CPwppZ6r1nCZNA9qsUSpjEntmRF19SEOb6mHKNPrCnIeknR", + "J5Ui4S+/LYKk81bqjO0S1hjXWpTaU0aW7XZJ88msPhL3bBc+WXKF0a46+RWK1+jpHrfhFzfak/bhTywK", + "JXlUbv7RBc8/tNT7s6WULdmf5odIzqtVN//JWmOrq4aRc2Q3ZIE0N6X2ZEkAaqAgEkKpiXups+C7ECGB", + "HmGF/I60CMa+QvGWPpXk/PNr+woF2OqyBFzJ14AOUmtykHqDSgowFnLpXNC3BeFdwi6DZRrVdTS2uuHZ", + "9W0uhepWqDcGhPA7zOhXzOmNfKLnpKfcPaZS6wJ2gBxai9sxRX8EJZ0PtCsqQQh0cHAv/TpEXVoQVJAW", + "pD1EmESnXpVWPVn/Lo0OqeVSp+Zxszq7eyqc5pnSqhGXBA5+KqUlwZa/9bVqXfNhlL+T3iytip66LZUm", + "iyuppN8+T8g7Vzwt6JuWaEiY6JzhEj2JPQL2wDAW0HqZYrS9yY9Ruz2ruiH+hZzDjEZSZc/rzcahjzuM", + "Gd5w0WDzPEBzLO0mrJF8HBs9raJg0r9jXMc+lrsOPzfao9RVpeOxftQVyUraEOTGxjpKbgaXadhlCdAS", + "aBPXEoBf6bOvKg/cS6VgRaClmsVy1vXJYedojboxHtWku3Zj1gXnjB11zVH/Qh5DZRjGhhuliPsJNcTq", + "nZUTK7dknayoMFg1VmZyfMlx1Jrs1Ln18rHDvczpOm4TYT01NkfPlkygpxdhkSUjSB44rEvIgWPylsuO", + "gbLj3l3CaiKOar7pX3lywrggjzLkqh76m9v6ZycH/T88Zntz9sAFldhjdlxeuCGXxpPXFmSgaGj7rJvB", + "zZocAUcdeHJ++xNcXriQwEygFkAVWAelIxGIWMtu+/mwotc+CQ/xcqyUDEwZryAyZgxdKpUwU5DGQrIl", + "+2G2mC0CtdGv491z1Ki2Trp5U4957cuMIpODe6sUJIKXwu4vdNHem3Sa+9/GMXLYMu81/7tkrPt3xnow", + "VlS9e6teuLpvT2Pfpvn2DF7ATWv9UGHWMluT860ZYN9zNKc4bixxtGL6FGXuwyH/KUhfX/8Me4nqr8m+", + "PxjA2pHztqR290+6zEM894bE2aA+vBXUfUb40GvrXy4WU4zc75v3G8Ndwv5xilyrC98l7J+niIx1xFH2", + "5UnXNSNK7AHKPEe7DR1vQ6EQitw4DzIvjPWoPXQQG8Tma0Ll118m4fvvuH6+Jn7Hxr15cgvVj85Iry6C", + "dD2H1jORdFDp2Lez0gx4UK0lUJm1WcybFmn+IGRGzu9O4+tr8j/WkhdUjHC1q/NFPDupFeBGizD1+Gok", + "alRopUSQOu6tEh+8LxeLHziqzFjp1/myUjV+pYYmIfMcWFLtOMqT/w0Nmnb7W8P/a+riPL5+YPMWwaF2", + "4DRMQrk4CR+3ceMfFSDDeepbREp/7joOlrrWzR/CVLlrYaSXyFLAGGWJKg6gCRR32TI1Zr7CmNIKdKHj", + "kToCQ4P0cG9KJSCVWgCqSszB6ByYyQ3p+tzmCaZ1/r/+Plsk8f2t/fHlbFE1UQM41wXvKg7Kj6D53ds3", + "L0hzI0hALQfv3r6BvwRt/joOyLD0PV8dReEh4JZUDHb91HcI9QQGp4raI4CcwmAYaPcQbGGvQaOuERqf", + "IdtJNracxtEMfjYW6DPmhaIEZNOJtq4+woUpGvRZ0D7iiRyAm7V0QFoURmoPlcnR/v2dg+rh4xATVTnO", + "oVM6hu8U+uolf8TNE2zq1/5nplG/+vw/sWissraJVL+vwe9Sc1WKzoT8O6QKs2CWIx9zYcD73rWonIFa", + "bNyH3ex0nLQntXFfhbXJ8Ikl+sE/zdro2eiwsAQNgSrfTR3U/I8wgiuEXHrXJRVI7TyhaO4cUeVj6fz+", + "lNnkpD8S8c6/+QSlWCrPlikqR/u5fmWMItTfG9Zhwzqa4Xa7/wYAAP//vcB68FQeAAA=", } // GetSwagger returns the content of the embedded swagger specification file diff --git a/pkg/guacrest/openapi.yaml b/pkg/guacrest/openapi.yaml index 6c26d50d4b..027e8868ae 100644 --- a/pkg/guacrest/openapi.yaml +++ b/pkg/guacrest/openapi.yaml @@ -28,39 +28,108 @@ paths: application/json: schema: type: string - "/query/dependencies": + "/analysis/dependencies": get: - summary: Retrieve transitive dependencies - description: > - Find the transitive dependencies of the input. The HasSBOM and HasSLSA - predicates are used as the dependency relationship and the IsOccurrence and - PkgEqual predicates are used to find consider equivalent packages. - operationId: retrieveDependencies + summary: Identify the most important dependencies + operationId: analyzeDependencies parameters: - $ref: "#/components/parameters/PaginationSpec" - - name: linkCondition - description: > - Whether links between nouns must be made by digest or if they - can be made just by name (i.e. purl). Specify 'name' to allow using - SBOMs that don't provide the digest of the subject. The default is - 'digest'. To search by purl, 'name' must be specified. + - name: sort + description: > + The sort order of the packages + * 'frequency' - The packages with the highest number of dependents + * 'scorecard' - The packages with the lowest OpenSSF scorecard score in: query - required: false + required: true schema: - type: string - enum: - - digest - - name + type: string + enum: + - frequency + - scorecard + responses: + "200": + $ref: "#/components/responses/PackageNameList" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerError" + "502": + $ref: "#/components/responses/BadGateway" + "/v0/package/{purl}": + get: + summary: Get all purls related to the given purl + description: > + If a partial purl, pkg:foo/bar is passed in, then it would find all purls + associated with the given purl, such as pkg:foo/bar@1.0, and pkg:foo/bar@2.0. + operationId: getPackagePurls + parameters: - name: purl - description: The purl of the dependent package. - in: query - required: false + in: path + required: true + description: URL-encoded Package URL (purl) schema: type: string - - name: digest - description: The digest of the dependent package. + responses: + "200": + $ref: "#/components/responses/PurlList" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerError" + "502": + $ref: "#/components/responses/BadGateway" + "/v0/package/{purl}/vulns": + get: + summary: Get vulnerabilities for a Package URL (purl) + description: > + If a partial purl is passed in, then it will find all associated purls + and find vulnerabilities of those. For example, if the partial purl + pkg:foo/bar is passed in, it would find all purls associated with + pkg:foo/bar, such as pkg:foo/bar@1.0, and pkg:foo/bar@2.0. This endpoint + will then find all vulnerabilities for these purls. If the `includeDependencies` + flag is set to true, it will also include vulnerabilities of the dependencies. + operationId: getPackageVulns + parameters: + - name: purl + in: path + required: true + description: URL-encoded Package URL (purl) + schema: + type: string + - name: includeDependencies in: query required: false + description: > + A flag to include vulnerabilities of the dependencies. If true, the + response will include vulnerabilities for the purl and its dependencies + instead of the vulnerabilities of just the purl. + schema: + type: boolean + default: false + responses: + "200": + $ref: "#/components/responses/VulnerabilityList" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerError" + "502": + $ref: "#/components/responses/BadGateway" + "/v0/package/{purl}/dependencies": + get: + summary: Get dependencies for a specific Package URL (purl) + description: > + If a partial purl is passed in, then it will find all associated purls + and find the dependencies of those. For example, if the partial purl + pkg:foo/bar is passed in, it would find all purls associated with + pkg:foo/bar, such as pkg:foo/bar@1.0, and pkg:foo/bar@2.0. This endpoint + will then find all dependencies for these purls. + operationId: getPackageDeps + parameters: + - name: purl + in: path + required: true + description: URL-encoded Package URL (purl) schema: type: string responses: @@ -72,34 +141,46 @@ paths: $ref: "#/components/responses/InternalServerError" "502": $ref: "#/components/responses/BadGateway" - "/analysis/dependencies": + "/v0/artifact/{digest}/vulns": get: - summary: Identify the most important dependencies - operationId: analyzeDependencies + summary: Get vulnerabilities for a specific digest + operationId: getArtifactVulns parameters: - - $ref: "#/components/parameters/PaginationSpec" - - name: sort - description: > - The sort order of the packages - * 'frequency' - The packages with the highest number of dependents - * 'scorecard' - The packages with the lowest OpenSSF scorecard score - in: query + - name: digest + in: path required: true + description: Digest, the second part from artifact identifier in the format schema: type: string - enum: - - frequency - - scorecard responses: "200": - $ref: "#/components/responses/PackageNameList" + $ref: "#/components/responses/VulnerabilityList" + "400": + $ref: "#/components/responses/BadRequest" + "500": + $ref: "#/components/responses/InternalServerError" + "502": + $ref: "#/components/responses/BadGateway" + "/v0/artifact/{digest}/dependencies": + get: + summary: Get dependencies for a specific digest + operationId: getArtifactDeps + parameters: + - name: digest + in: path + required: true + description: Digest, the second part from artifact identifier in the format + schema: + type: string + responses: + "200": + $ref: "#/components/responses/PurlList" "400": $ref: "#/components/responses/BadRequest" "500": $ref: "#/components/responses/InternalServerError" "502": $ref: "#/components/responses/BadGateway" - components: parameters: @@ -148,6 +229,51 @@ components: $ref: "#/components/schemas/Purl" DependentCount: type: integer + Vulnerability: + type: object + required: + - package + - vulnerability + - metadata + properties: + package: + type: string + vulnerability: + $ref: '#/components/schemas/VulnerabilityDetails' + metadata: + $ref: '#/components/schemas/ScanMetadata' + VulnerabilityDetails: + type: object + required: + - vulnerabilityIDs + properties: + type: + type: string + vulnerabilityIDs: + type: array + items: + type: string + description: > + A list of vulnerability identifiers. These can be CVE IDs or other + formats used to identify vulnerabilities. + ScanMetadata: + type: object + properties: + dbUri: + type: string + dbVersion: + type: string + scannerUri: + type: string + scannerVersion: + type: string + timeScanned: + type: string + format: date-time + origin: + type: string + collector: + type: string responses: # for code 200 PurlList: @@ -174,6 +300,22 @@ components: type: array items: $ref: "#/components/schemas/PackageName" + VulnerabilityList: + description: A list of vulnerabilities associated with the package or artifact + content: + application/json: + schema: + type: array + items: + $ref: "#/components/schemas/Vulnerability" + DependencyList: + description: A list of dependencies associated with the package or artifact + content: + application/json: + schema: + type: array + items: + type: string # intended for code 400, client side error BadRequest: description: Bad request, such as from invalid or missing parameters diff --git a/pkg/guacrest/server/errors.go b/pkg/guacrest/server/errors.go index b90aea45ac..e31f5647be 100644 --- a/pkg/guacrest/server/errors.go +++ b/pkg/guacrest/server/errors.go @@ -17,32 +17,158 @@ package server import ( "context" + "net/http" gen "github.com/guacsec/guac/pkg/guacrest/generated" "github.com/guacsec/guac/pkg/guacrest/helpers" ) -// Maps helpers.Err502 and helpers.Err500 to the corresponding OpenAPI response type. -// Other errors are returned as Client errors. -func handleErr(ctx context.Context, err error) gen.RetrieveDependenciesResponseObject { +// Define a custom type for endpoint types +type EndpointType int + +const ( + GetPackagePurls EndpointType = iota + GetPackageVulns + GetPackageDeps + GetArtifactVulns + GetArtifactDeps +) + +// IsErrorResponse checks if the response status code indicates an error. +func IsErrorResponse(statusCode int) bool { + return statusCode >= 400 +} + +// IsSuccessResponse checks if the response status code indicates success. +func IsSuccessResponse(statusCode int) bool { + return statusCode == http.StatusOK +} + +func handleErr( + ctx context.Context, + err error, + endpointType EndpointType, +) interface{} { if err == nil { - return nil + return createBadRequestResponse(endpointType, "Unknown error") } switch err { case helpers.Err502: - return gen.RetrieveDependencies502JSONResponse{ - BadGatewayJSONResponse: gen.BadGatewayJSONResponse{ - Message: err.Error(), - }} + return createBadGatewayResponse(endpointType, err.Error()) case helpers.Err500: - return gen.RetrieveDependencies500JSONResponse{ - InternalServerErrorJSONResponse: gen.InternalServerErrorJSONResponse{ - Message: err.Error(), - }} + return createInternalServerErrorResponse(endpointType, err.Error()) default: - return gen.RetrieveDependencies400JSONResponse{ + return createBadRequestResponse(endpointType, err.Error()) + } +} + +func createBadRequestResponse(endpointType EndpointType, message string) interface{} { + switch endpointType { + case GetPackagePurls: + return gen.GetPackagePurls400JSONResponse{ + BadRequestJSONResponse: gen.BadRequestJSONResponse{ + Message: message, + }, + } + case GetPackageDeps: + return gen.GetPackageDeps400JSONResponse{ + BadRequestJSONResponse: gen.BadRequestJSONResponse{ + Message: message, + }, + } + case GetPackageVulns: + return gen.GetPackageVulns400JSONResponse{ BadRequestJSONResponse: gen.BadRequestJSONResponse{ - Message: err.Error(), - }} + Message: message, + }, + } + case GetArtifactDeps: + return gen.GetArtifactDeps400JSONResponse{ + BadRequestJSONResponse: gen.BadRequestJSONResponse{ + Message: message, + }, + } + case GetArtifactVulns: + return gen.GetArtifactVulns400JSONResponse{ + BadRequestJSONResponse: gen.BadRequestJSONResponse{ + Message: message, + }, + } + default: + return nil + } +} + +func createInternalServerErrorResponse(endpointType EndpointType, message string) interface{} { + switch endpointType { + case GetPackagePurls: + return gen.GetPackagePurls500JSONResponse{ + InternalServerErrorJSONResponse: gen.InternalServerErrorJSONResponse{ + Message: message, + }, + } + case GetPackageDeps: + return gen.GetPackageDeps500JSONResponse{ + InternalServerErrorJSONResponse: gen.InternalServerErrorJSONResponse{ + Message: message, + }, + } + case GetPackageVulns: + return gen.GetPackageVulns500JSONResponse{ + InternalServerErrorJSONResponse: gen.InternalServerErrorJSONResponse{ + Message: message, + }, + } + case GetArtifactDeps: + return gen.GetArtifactDeps500JSONResponse{ + InternalServerErrorJSONResponse: gen.InternalServerErrorJSONResponse{ + Message: message, + }, + } + case GetArtifactVulns: + return gen.GetArtifactVulns500JSONResponse{ + InternalServerErrorJSONResponse: gen.InternalServerErrorJSONResponse{ + Message: message, + }, + } + default: + return nil + } +} + +func createBadGatewayResponse(endpointType EndpointType, message string) interface{} { + switch endpointType { + case GetPackagePurls: + return gen.GetPackagePurls502JSONResponse{ + BadGatewayJSONResponse: gen.BadGatewayJSONResponse{ + Message: message, + }, + } + case GetPackageDeps: + return gen.GetPackageDeps502JSONResponse{ + BadGatewayJSONResponse: gen.BadGatewayJSONResponse{ + Message: message, + }, + } + case GetPackageVulns: + return gen.GetPackageVulns502JSONResponse{ + BadGatewayJSONResponse: gen.BadGatewayJSONResponse{ + Message: message, + }, + } + case GetArtifactDeps: + return gen.GetArtifactDeps502JSONResponse{ + BadGatewayJSONResponse: gen.BadGatewayJSONResponse{ + Message: message, + }, + } + case GetArtifactVulns: + return gen.GetArtifactVulns502JSONResponse{ + BadGatewayJSONResponse: gen.BadGatewayJSONResponse{ + Message: message, + }, + } + default: + return nil } } diff --git a/pkg/guacrest/server/retrieveDependencies.go b/pkg/guacrest/server/retrieveDependencies.go index db09f8e367..f3891809d2 100644 --- a/pkg/guacrest/server/retrieveDependencies.go +++ b/pkg/guacrest/server/retrieveDependencies.go @@ -1,34 +1,49 @@ +// +// Copyright 2024 The GUAC Authors. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package server import ( "context" "fmt" + "net/url" - "github.com/Khan/genqlient/graphql" gql "github.com/guacsec/guac/pkg/assembler/clients/generated" assembler_helpers "github.com/guacsec/guac/pkg/assembler/helpers" - gen "github.com/guacsec/guac/pkg/guacrest/generated" "github.com/guacsec/guac/pkg/guacrest/helpers" - "github.com/guacsec/guac/pkg/guacrest/pagination" "github.com/guacsec/guac/pkg/logging" + + "github.com/Khan/genqlient/graphql" "golang.org/x/exp/maps" ) -// node is implemented by all graphQL client types +// node is implemented by all GraphQL client types type node interface { GetId() string } // edgeGen defines the edges used by the transitive dependencies graph traversal. type edgeGen interface { - // getDirectDependencies returns the nouns that are direct dependencies of the input noun. + // getDirectDependencies returns the nodes that are direct dependencies of the input noun. getDirectDependencies(ctx context.Context, v node) ([]node, error) - // getEquivalentNodes returns the nouns that are considered equivalent to the input noun. + // getEquivalentNodes returns the nodes that are considered equivalent to the input noun. getEquivalentNodes(ctx context.Context, v node) ([]node, error) } -// byDigest is an edgeGen that observes relationships between nouns when they are +// byDigest is a edgeGen that observes relationships between noun when they are // linked by digest. // // The dependency edges are: @@ -73,7 +88,7 @@ func newByName(gqlClient graphql.Client) byName { return byName{gqlClient: gqlClient, bd: newByDigest(gqlClient)} } -/********* The graph traversal *********/ +//********* The graph traversal *********/ func getTransitiveDependencies( ctx context.Context, @@ -92,27 +107,28 @@ func getTransitiveDependencies( nodesEquivalentToStart := map[node]any{start: struct{}{}} for len(queue) > 0 { - node := queue[0] + currentNode := queue[0] queue = queue[1:] - if _, ok := visited[node.GetId()]; ok { + if _, ok := visited[currentNode.GetId()]; ok { continue } - visited[node.GetId()] = node + visited[currentNode.GetId()] = currentNode - adjacent, err := edges.getDirectDependencies(ctx, node) + // Get direct dependencies + adjacent, err := edges.getDirectDependencies(ctx, currentNode) if err != nil { return nil, err } queue = append(queue, adjacent...) - adjacent, err = edges.getEquivalentNodes(ctx, node) + adjacent, err = edges.getEquivalentNodes(ctx, currentNode) if err != nil { return nil, err } queue = append(queue, adjacent...) - if _, ok := nodesEquivalentToStart[node]; ok { + if _, ok := nodesEquivalentToStart[currentNode]; ok { for _, equivalentNode := range adjacent { nodesEquivalentToStart[equivalentNode] = struct{}{} } @@ -180,10 +196,10 @@ func (eg byName) getEquivalentNodes(ctx context.Context, v node) ([]node, error) return neighborsTwoHops(ctx, eg.gqlClient, v, edgesToPredicates, edgesFromPredicates) } -/********* Graphql helper functions *********/ +//********* GraphQL helper functions *********/ -// neighborsTwoHops calls the GraphQL Neighbors endpoint once with edgesToPredicates, and -// then again on the result with edgesFromPredicates. +// neighborsTwoHops calls the GraphQL Neighbors endpoint once with edgesToPredicates, +// and then again on the result with edgesFromPredicates. func neighborsTwoHops(ctx context.Context, gqlClient graphql.Client, v node, edgesToPredicates []gql.Edge, edgesFromPredicates []gql.Edge) ([]node, error) { predicates, err := neighbors(ctx, gqlClient, v, edgesToPredicates) @@ -207,7 +223,7 @@ func neighbors(ctx context.Context, gqlClient graphql.Client, v node, edges []gq logger := logging.FromContext(ctx) neighborsResponse, err := gql.Neighbors(ctx, gqlClient, v.GetId(), edges) if err != nil { - logger.Errorf("Neighbors query returned err: ", err) + logger.Errorf("Neighbors query returned err: %v", err) return nil, helpers.Err502 } if neighborsResponse == nil { @@ -230,9 +246,9 @@ func transformWithError[A any, B any](ctx context.Context, lst []A, f func(conte return res, nil } -// Returns the graphQL type that is nested in the neighbors response node. For package tries, -// the leaf version node is returned. Only the types relevant to the retrieveDependecies -// graph traversal are implemented. +// neighborToNode returns the GraphQL type that is nested in the neighbors response node. +// For package trees, the leaf version node is returned. Only the types relevant +// to the retrieveDependencies graph traversal are implemented. func neighborToNode(ctx context.Context, neighborsNode gql.NeighborsNeighborsNode) (node, error) { logger := logging.FromContext(ctx) switch val := neighborsNode.(type) { @@ -288,10 +304,9 @@ func neighborToNode(ctx context.Context, neighborsNode gql.NeighborsNeighborsNod return nil, helpers.Err500 } -// Maps nodes in the input to purls, ignoring nodes that are not package version -// nodes. +// Maps nodes in the input to a map of "pkg id : purl", ignoring nodes that are not package version nodes. func mapPkgNodesToPurls(ctx context.Context, gqlClient graphql.Client, - nodes []node) ([]string, error) { + nodes []node) (map[string]string, error) { logger := logging.FromContext(ctx) // get the IDs of the package nodes @@ -320,75 +335,80 @@ func mapPkgNodesToPurls(ctx context.Context, gqlClient graphql.Client, logger.Warnf("GQL query \"nodes\" did not return the expected number of results") } - // map the package tries to purls - purls := make([]string, 0, len(gqlNodes.GetNodes())) + // map the package tries to a map of "pkg id : purl" + purlMap := make(map[string]string) for _, gqlNode := range gqlNodes.GetNodes() { if v, ok := gqlNode.(*gql.NodesNodesPackage); ok { purl := assembler_helpers.AllPkgTreeToPurl(&v.AllPkgTree) - purls = append(purls, purl) + purlMap[v.AllPkgTree.Namespaces[0].Names[0].Versions[0].Id] = purl } else { logger.Warnf("Nodes query returned an unexpected type: %T", *gqlNode.GetTypename()) } } - return purls, nil + return purlMap, nil } -/********* The endpoint handler *********/ -func (s *DefaultServer) RetrieveDependencies( +// GetDepsForPackage gets all direct and transitive dependencies for a given purl +func GetDepsForPackage( ctx context.Context, - request gen.RetrieveDependenciesRequestObject, -) (gen.RetrieveDependenciesResponseObject, error) { + gqlClient graphql.Client, + purl string, +) (map[string]string, error) { // Find the start node - var start node - if request.Params.Purl != nil { - pkg, err := helpers.FindPackageWithPurl(ctx, s.gqlClient, *request.Params.Purl) - if err != nil { - return handleErr(ctx, err), nil - } - start = &pkg - } else if request.Params.Digest != nil { - artifact, err := helpers.FindArtifactWithDigest(ctx, s.gqlClient, *request.Params.Digest) - if err != nil { - return handleErr(ctx, err), nil - } - start = &artifact - } else { - return gen.RetrieveDependencies400JSONResponse{ - BadRequestJSONResponse: gen.BadRequestJSONResponse{ - Message: "Neither a purl or a digest argument was provided", - }}, nil + unescapedPurl, err := url.QueryUnescape(purl) + if err != nil { + return nil, fmt.Errorf("failed to unescape package url: %w", err) + } + pkg, err := helpers.FindPackageWithPurl(ctx, gqlClient, unescapedPurl) + if err != nil { + return nil, fmt.Errorf("failed to find package with purl: %w", err) } - // Select the edgeGen. The default is byDigest - var edgeGenerator edgeGen - cond := request.Params.LinkCondition - if cond == nil { - edgeGenerator = newByDigest(s.gqlClient) - } else if *cond == gen.Name { - edgeGenerator = newByName(s.gqlClient) - } else if *cond == gen.Digest { - edgeGenerator = newByDigest(s.gqlClient) - } else { - err := fmt.Errorf("Unrecognized linkCondition: %s", *request.Params.LinkCondition) - return handleErr(ctx, err), nil + edgeGenerator := newByName(gqlClient) + // Perform traversal + deps, err := getTransitiveDependencies(ctx, gqlClient, &pkg, edgeGenerator) + if err != nil { + return nil, fmt.Errorf("failed to get dependencies: %w", err) + } + + // Map nodes to PURLs + purls, err := mapPkgNodesToPurls(ctx, gqlClient, deps) + if err != nil { + return nil, fmt.Errorf("failed to map dependencies: %w", err) } - // Compute the result and map to purls - deps, err := getTransitiveDependencies(ctx, s.gqlClient, start, edgeGenerator) + return purls, nil +} + +// GetDepsForArtifact gets all direct and transitive dependencies for a digest +func GetDepsForArtifact( + ctx context.Context, + gqlClient graphql.Client, + digest string, +) (map[string]string, error) { + // Find the start node + unescapedDigest, err := url.QueryUnescape(digest) + if err != nil { + return nil, fmt.Errorf("failed to unescape digest: %w", err) + } + art, err := helpers.FindArtifactWithDigest(ctx, gqlClient, unescapedDigest) if err != nil { - return handleErr(ctx, err), nil + return nil, fmt.Errorf("failed to find digest: %w", err) } - purls, err := mapPkgNodesToPurls(ctx, s.gqlClient, deps) + + edgeGenerator := newByDigest(gqlClient) + + // Perform traversal + deps, err := getTransitiveDependencies(ctx, gqlClient, &art, edgeGenerator) if err != nil { - return handleErr(ctx, err), nil + return nil, fmt.Errorf("failed to get dependencies: %w", err) } - page, pageInfo, err := pagination.Paginate(ctx, purls, request.Params.PaginationSpec) + // Map nodes to PURLs + purls, err := mapPkgNodesToPurls(ctx, gqlClient, deps) if err != nil { - return handleErr(ctx, err), nil + return nil, fmt.Errorf("failed to map dependencies: %w", err) } - return gen.RetrieveDependencies200JSONResponse{PurlListJSONResponse: gen.PurlListJSONResponse{ - PurlList: page, - PaginationInfo: pageInfo, - }}, nil + + return purls, nil } diff --git a/pkg/guacrest/server/retrieveDependencies_test.go b/pkg/guacrest/server/retrieveDependencies_test.go index 3fe0b71076..b1fad57053 100644 --- a/pkg/guacrest/server/retrieveDependencies_test.go +++ b/pkg/guacrest/server/retrieveDependencies_test.go @@ -1,34 +1,31 @@ -// // Copyright 2024 The GUAC Authors. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // -// http://www.apache.org/licenses/LICENSE-2.0 +// http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. - package server_test import ( stdcmp "cmp" "context" + "net/url" "testing" - cmp "github.com/google/go-cmp/cmp" - - "github.com/google/go-cmp/cmp/cmpopts" . "github.com/guacsec/guac/internal/testing/graphqlClients" - "github.com/guacsec/guac/internal/testing/ptrfrom" _ "github.com/guacsec/guac/pkg/assembler/backends/keyvalue" - api "github.com/guacsec/guac/pkg/guacrest/generated" "github.com/guacsec/guac/pkg/guacrest/server" "github.com/guacsec/guac/pkg/logging" + + "github.com/google/go-cmp/cmp" + "github.com/google/go-cmp/cmp/cmpopts" ) // Tests the edges in ByName that are not in ByDigest @@ -38,8 +35,9 @@ func Test_RetrieveDependencies(t *testing.T) { name string data GuacData - // Only specify Purl or Digest. The test will set linkCondition, because both are tested - input api.RetrieveDependenciesParams + // Only specify a Purl or a Digest. But, both purls and digests are tested + purl string + digest string expectedByName []string expectedByDigest []string @@ -60,7 +58,7 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "pkg:guac/foo", IncludedSoftware: []string{"pkg:guac/bar"}}, }, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", expectedByName: []string{"pkg:guac/bar"}, expectedByDigest: []string{}, }, @@ -72,7 +70,7 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "pkg:guac/foo", IncludedSoftware: []string{"pkg:guac/bar", "pkg:guac/baz"}}, }, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", expectedByName: []string{"pkg:guac/bar", "pkg:guac/baz"}, expectedByDigest: []string{}, }, @@ -85,7 +83,7 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "sha-xyz", IncludedSoftware: []string{"pkg:guac/bar"}}, }, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-xyz")}, + digest: "sha-xyz", expectedByName: []string{"pkg:guac/bar"}, expectedByDigest: []string{"pkg:guac/bar"}, }, @@ -98,12 +96,12 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "pkg:guac/bar", IncludedSoftware: []string{"pkg:guac/baz"}}, }, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", expectedByName: []string{"pkg:guac/bar", "pkg:guac/baz"}, expectedByDigest: []string{}, }, { - name: "Artifact -> SBOM -> artifact -> SBOM -> package", + name: "Artifact -> SBOM -> digest -> SBOM -> package", data: GuacData{ Packages: []string{"pkg:guac/foo"}, Artifacts: []string{"sha-xyz", "sha-123"}, @@ -112,7 +110,7 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "sha-123", IncludedSoftware: []string{"pkg:guac/foo"}}, }, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-xyz")}, + digest: "sha-xyz", expectedByName: []string{"pkg:guac/foo"}, expectedByDigest: []string{"pkg:guac/foo"}, }, @@ -126,36 +124,36 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "pkg:guac/bar", IncludedSoftware: []string{"pkg:guac/foo"}}, }, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-xyz")}, + digest: "sha-xyz", expectedByName: []string{"pkg:guac/foo", "pkg:guac/bar"}, expectedByDigest: []string{"pkg:guac/bar"}, }, { - name: "artifact -> occurrence -> package", + name: "digest -> occurrence -> package", data: GuacData{ Packages: []string{"pkg:guac/bar"}, Artifacts: []string{"sha-123", "sha-xyz"}, HasSboms: []HasSbom{{Subject: "sha-xyz", IncludedSoftware: []string{"sha-123"}}}, IsOccurrences: []IsOccurrence{{Subject: "pkg:guac/bar", Artifact: "sha-123"}}, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-xyz")}, + digest: "sha-xyz", expectedByName: []string{"pkg:guac/bar"}, expectedByDigest: []string{"pkg:guac/bar"}, }, { - name: "Package -> occurrence -> artifact", + name: "Package -> occurrence -> digest", data: GuacData{ Packages: []string{"pkg:guac/foo", "pkg:guac/bar"}, Artifacts: []string{"sha-xyz"}, HasSboms: []HasSbom{{Subject: "sha-xyz", IncludedSoftware: []string{"pkg:guac/bar"}}}, IsOccurrences: []IsOccurrence{{Subject: "pkg:guac/foo", Artifact: "sha-xyz"}}, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", expectedByName: []string{"pkg:guac/bar"}, expectedByDigest: []string{}, }, { - name: "package -> occurrence -> artifact, artifact", + name: "package -> occurrence -> digest, digest", data: GuacData{ Packages: []string{"pkg:guac/foo", "pkg:guac/bar", "pkg:guac/baz"}, Artifacts: []string{"sha-xyz", "sha-123"}, @@ -168,24 +166,24 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "pkg:guac/foo", Artifact: "sha-123"}, }, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", expectedByName: []string{"pkg:guac/bar", "pkg:guac/baz"}, expectedByDigest: []string{}, }, { - name: "Artifact -> hashEqual -> artifact", + name: "Artifact -> hashEqual -> digest", data: GuacData{ Packages: []string{"pkg:guac/foo"}, Artifacts: []string{"sha-123", "sha-456"}, HasSboms: []HasSbom{{Subject: "sha-456", IncludedSoftware: []string{"pkg:guac/foo"}}}, HashEquals: []HashEqual{{ArtifactA: "sha-123", ArtifactB: "sha-456"}}, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-123")}, + digest: "sha-123", expectedByName: []string{"pkg:guac/foo"}, expectedByDigest: []string{"pkg:guac/foo"}, }, { - name: "Artifact -> hashEqual -> artifact, artifact", + name: "Artifact -> hashEqual -> digest, digest", data: GuacData{ Packages: []string{"pkg:guac/foo", "pkg:guac/bar"}, Artifacts: []string{"sha-123", "sha-456", "sha-789"}, @@ -198,12 +196,12 @@ func Test_RetrieveDependencies(t *testing.T) { {ArtifactA: "sha-123", ArtifactB: "sha-789"}, }, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-123")}, + digest: "sha-123", expectedByName: []string{"pkg:guac/foo", "pkg:guac/bar"}, expectedByDigest: []string{"pkg:guac/foo", "pkg:guac/bar"}, }, { - name: "Artifact -> hashEqual -> artifact -> hashEqual -> artifact", + name: "Artifact -> hashEqual -> digest -> hashEqual -> digest", data: GuacData{ Packages: []string{"pkg:guac/foo", "pkg:guac/bar"}, Artifacts: []string{"sha-123", "sha-456", "sha-789"}, @@ -216,12 +214,12 @@ func Test_RetrieveDependencies(t *testing.T) { {ArtifactA: "sha-456", ArtifactB: "sha-789"}, }, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-123")}, + digest: "sha-123", expectedByName: []string{"pkg:guac/foo", "pkg:guac/bar"}, expectedByDigest: []string{"pkg:guac/foo", "pkg:guac/bar"}, }, { - name: "artifact -> SLSA -> artifact -> occurrence -> package", + name: "digest -> SLSA -> digest -> occurrence -> package", data: GuacData{ Packages: []string{"pkg:guac/foo"}, Artifacts: []string{"sha-123", "sha-xyz"}, @@ -229,12 +227,12 @@ func Test_RetrieveDependencies(t *testing.T) { IsOccurrences: []IsOccurrence{{Subject: "pkg:guac/foo", Artifact: "sha-xyz"}}, HasSlsas: []HasSlsa{{Subject: "sha-123", BuiltBy: "GHA", BuiltFrom: []string{"sha-xyz"}}}, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-123")}, + digest: "sha-123", expectedByName: []string{"pkg:guac/foo"}, expectedByDigest: []string{"pkg:guac/foo"}, }, { - name: "artifact -> SLSA -> artifact, artifact", + name: "digest -> SLSA -> digest, digest", data: GuacData{ Packages: []string{"pkg:guac/foo", "pkg:guac/bar"}, Artifacts: []string{"sha-123", "sha-xyz", "sha-abc"}, @@ -245,7 +243,7 @@ func Test_RetrieveDependencies(t *testing.T) { }, HasSlsas: []HasSlsa{{Subject: "sha-123", BuiltBy: "GHA", BuiltFrom: []string{"sha-xyz", "sha-abc"}}}, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-123")}, + digest: "sha-123", expectedByName: []string{"pkg:guac/foo", "pkg:guac/bar"}, expectedByDigest: []string{"pkg:guac/foo", "pkg:guac/bar"}, }, @@ -253,7 +251,7 @@ func Test_RetrieveDependencies(t *testing.T) { * Test some edge cases *******/ { - name: "Both Package and occurrence artifact in SBOM does not lead to duplicate packages", + name: "Both Package and occurrence digest in SBOM does not lead to duplicate packages", data: GuacData{ Packages: []string{"pkg:guac/bar"}, Artifacts: []string{"sha-123", "sha-xyz"}, @@ -263,7 +261,7 @@ func Test_RetrieveDependencies(t *testing.T) { IncludedIsOccurrences: []IsOccurrence{{Subject: "pkg:guac/bar", Artifact: "sha-123"}}, }}, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-xyz")}, + digest: "sha-xyz", expectedByName: []string{"pkg:guac/bar"}, expectedByDigest: []string{"pkg:guac/bar"}, }, @@ -276,7 +274,7 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "pkg:guac/baz", IncludedSoftware: []string{"pkg:guac/bar"}}, }, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", expectedByName: []string{"pkg:guac/bar"}, expectedByDigest: []string{}, }, @@ -289,7 +287,7 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "pkg:guac/bar", IncludedSoftware: []string{"pkg:guac/baz"}}, }, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/baz")}, + purl: "pkg:guac/baz", expectedByName: []string{}, expectedByDigest: []string{}, }, @@ -305,7 +303,7 @@ func Test_RetrieveDependencies(t *testing.T) { IncludedSoftware: []string{"pkg:guac/bar"}, IncludedIsOccurrences: []IsOccurrence{{Subject: "pkg:guac/bar", Artifact: "sha-123"}}, }, - // an artifact with digest sha-xyz depends on baz + // an digest with digest sha-xyz depends on baz { Subject: "sha-xyz", IncludedSoftware: []string{"pkg:guac/baz"}, @@ -314,7 +312,7 @@ func Test_RetrieveDependencies(t *testing.T) { // sha-xyz is an occurrence of bar IsOccurrences: []IsOccurrence{{Subject: "pkg:guac/bar", Artifact: "sha-xyz"}}, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", // foo depends on baz expectedByName: []string{"pkg:guac/bar", "pkg:guac/baz"}, expectedByDigest: []string{}, @@ -329,7 +327,7 @@ func Test_RetrieveDependencies(t *testing.T) { Artifacts: []string{"sha-123"}, IsOccurrences: []IsOccurrence{{Subject: "pkg:guac/foo", Artifact: "sha-123"}}, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-123")}, + digest: "sha-123", expectedByName: []string{}, expectedByDigest: []string{}, }, @@ -343,7 +341,7 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "pkg:guac/bar", Artifact: "sha-123"}, }, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", expectedByName: []string{}, expectedByDigest: []string{}, }, @@ -355,7 +353,7 @@ func Test_RetrieveDependencies(t *testing.T) { HashEquals: []HashEqual{{ArtifactA: "sha-123", ArtifactB: "sha-456"}}, IsOccurrences: []IsOccurrence{{Subject: "pkg:guac/foo", Artifact: "sha-456"}}, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-123")}, + digest: "sha-123", expectedByName: []string{}, expectedByDigest: []string{}, }, @@ -374,7 +372,7 @@ func Test_RetrieveDependencies(t *testing.T) { }, IsOccurrences: []IsOccurrence{{Subject: "pkg:guac/foo", Artifact: "sha-789"}}, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-123")}, + digest: "sha-123", expectedByName: []string{}, expectedByDigest: []string{}, }, @@ -390,7 +388,7 @@ func Test_RetrieveDependencies(t *testing.T) { }, IsOccurrences: []IsOccurrence{{Subject: "pkg:guac/foo", Artifact: "sha-789"}}, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-123")}, + digest: "sha-123", expectedByName: []string{"pkg:guac/foo"}, expectedByDigest: []string{"pkg:guac/foo"}, }, @@ -406,7 +404,7 @@ func Test_RetrieveDependencies(t *testing.T) { }, IsOccurrences: []IsOccurrence{{Subject: "pkg:guac/foo", Artifact: "sha-789"}}, }, - input: api.RetrieveDependenciesParams{Digest: ptrfrom.String("sha-123")}, + digest: "sha-123", expectedByName: []string{"pkg:guac/foo"}, expectedByDigest: []string{"pkg:guac/foo"}, }, @@ -422,7 +420,7 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "pkg:guac/foo@v1", IncludedSoftware: []string{"pkg:guac/bar"}}, }, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", expectedByName: []string{"pkg:guac/bar@v1"}, expectedByDigest: []string{}, }, @@ -435,7 +433,7 @@ func Test_RetrieveDependencies(t *testing.T) { {Subject: "pkg:guac/foo@v1", IncludedSoftware: []string{"pkg:guac/bar@v1"}}, }, }, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", expectedByName: []string{"pkg:guac/bar"}, expectedByDigest: []string{}, }, @@ -453,7 +451,7 @@ func Test_RetrieveDependencies(t *testing.T) { Subject: "pkg:oci/debian@sha256%3A244fd47e07d10?repository_url=ghcr.io&tag=bullseye", IncludedSoftware: []string{"pkg:oci/static@sha256%3A244fd47e07d10?repository_url=gcr.io%2Fdistroless&tag=latest"}}, }}, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:oci/debian@sha256%3A244fd47e07d10?repository_url=ghcr.io&tag=bullseye")}, + purl: "pkg:oci/debian@sha256%3A244fd47e07d10?repository_url=ghcr.io&tag=bullseye", expectedByName: []string{"pkg:oci/static@sha256%3A244fd47e07d10?repository_url=gcr.io%2Fdistroless&tag=latest"}, expectedByDigest: []string{}, }, @@ -469,7 +467,7 @@ func Test_RetrieveDependencies(t *testing.T) { Subject: "pkg:guac/foo", IncludedSoftware: []string{"pkg:github/Package-url/purl-Spec"}}, }}, - input: api.RetrieveDependenciesParams{Purl: ptrfrom.String("pkg:guac/foo")}, + purl: "pkg:guac/foo", expectedByName: []string{"pkg:github/package-url/purl-spec"}, // lowercased expectedByDigest: []string{}, }, @@ -482,151 +480,41 @@ func Test_RetrieveDependencies(t *testing.T) { gqlClient := SetupTest(t) Ingest(ctx, t, gqlClient, tt.data) - restApi := server.NewDefaultServer(gqlClient) + encodedPurl := url.QueryEscape(tt.purl) + encodedArtifact := url.QueryEscape(tt.digest) - /******** call the endpoint with byName link condition ********/ - inputByName := tt.input - inputByName.LinkCondition = ptrfrom.Any(api.Name) - resByName, err := restApi.RetrieveDependencies(ctx, api.RetrieveDependenciesRequestObject{Params: inputByName}) - if err != nil { - t.Fatalf("Endpoint returned unexpected error: %v", err) - } - /******** check the output ********/ - switch v := resByName.(type) { - case api.RetrieveDependencies200JSONResponse: - if !cmp.Equal(v.PurlList, tt.expectedByName, cmpopts.EquateEmpty(), cmpopts.SortSlices(stdcmp.Less[string])) { - t.Errorf("RetrieveDependencies with byName returned %v, but wanted %v", v.PurlList, tt.expectedByName) + if tt.purl != "" { + /******** call the endpoint with byName link condition ********/ + resByName, err := server.GetDepsForPackage(ctx, gqlClient, encodedPurl) + if err != nil { + t.Fatalf("Endpoint returned unexpected error: %v", err) } - default: - t.Errorf("RetrieveDependencies with byName returned unexpected error: %v", v) - } - - /******** call the endpoint with byDigest link condition ********/ - inputByDigest := tt.input - inputByDigest.LinkCondition = ptrfrom.Any(api.Digest) - resByDigest, err := restApi.RetrieveDependencies(ctx, api.RetrieveDependenciesRequestObject{Params: inputByDigest}) - if err != nil { - t.Fatalf("Endpoint returned unexpected error: %v", err) - } - /******** check the output ********/ - switch v := resByDigest.(type) { - case api.RetrieveDependencies200JSONResponse: - if !cmp.Equal(v.PurlList, tt.expectedByDigest, cmpopts.EquateEmpty(), cmpopts.SortSlices(stdcmp.Less[string])) { - t.Errorf("RetrieveDependencies with byDigest returned %v, but wanted %v", v.PurlList, tt.expectedByDigest) + /******** check the output ********/ + var purls []string + for _, purl := range resByName { + purls = append(purls, purl) + } + if !cmp.Equal(purls, tt.expectedByName, cmpopts.EquateEmpty(), cmpopts.SortSlices(stdcmp.Less[string])) { + t.Errorf("RetrieveDependencies with byName returned %v, but wanted %v", purls, tt.expectedByName) } - default: - t.Errorf("RetrieveDependencies with byDigest returned unexpected error: %v", v) - } - }) - } -} - -func Test_ClientErrors(t *testing.T) { - ctx := logging.WithLogger(context.Background()) - tests := []struct { - name string - data GuacData - input api.RetrieveDependenciesParams - }{{ - name: "Package not found", - input: api.RetrieveDependenciesParams{ - Purl: ptrfrom.String("pkg:guac/foo"), - LinkCondition: ptrfrom.Any(api.Name), - }, - }, { - name: "Package not found because version was specified", - data: GuacData{Packages: []string{"pkg:guac/foo"}}, - input: api.RetrieveDependenciesParams{ - Purl: ptrfrom.String("pkg:guac/foo@v1"), - LinkCondition: ptrfrom.Any(api.Name), - }, - }, { - name: "Package not found because version was not specified", - data: GuacData{Packages: []string{"pkg:guac/foo@v1"}}, - input: api.RetrieveDependenciesParams{ - Purl: ptrfrom.String("pkg:guac/foo"), - LinkCondition: ptrfrom.Any(api.Name), - }, - }, { - name: "Package not found due to missing qualifiers", - data: GuacData{Packages: []string{"pkg:guac/foo?a=b"}}, - input: api.RetrieveDependenciesParams{ - Purl: ptrfrom.String("pkg:guac/foo"), - LinkCondition: ptrfrom.Any(api.Name), - }, - }, { - name: "Package not found due to providing qualifiers", - data: GuacData{Packages: []string{"pkg:guac/foo"}}, - input: api.RetrieveDependenciesParams{ - Purl: ptrfrom.String("pkg:guac/foo?a=b"), - LinkCondition: ptrfrom.Any(api.Name), - }, - }, { - name: "Artifact not found because version was not specified", - input: api.RetrieveDependenciesParams{ - Digest: ptrfrom.String("sha-abc"), - LinkCondition: ptrfrom.Any(api.Name), - }, - }, { - name: "Neither Purl nor Digest provided", - }, { - name: "Unrecognized link condition", - input: api.RetrieveDependenciesParams{ - Digest: ptrfrom.String("sha-abc"), - LinkCondition: ptrfrom.Any(api.RetrieveDependenciesParamsLinkCondition("foo")), - }, - }} - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gqlClient := SetupTest(t) - Ingest(ctx, t, gqlClient, tt.data) - restApi := server.NewDefaultServer(gqlClient) - res, err := restApi.RetrieveDependencies(ctx, api.RetrieveDependenciesRequestObject{Params: tt.input}) - if err != nil { - t.Fatalf("RetrieveDependencies returned unexpected error: %v", err) - } - if _, ok := res.(api.RetrieveDependencies400JSONResponse); !ok { - t.Fatalf("Did not receive a 400 Response: recieved %v of type %T", res, res) } + if tt.digest != "" { + /******** call the endpoint with byDigest link condition ********/ + resByDigest, err := server.GetDepsForArtifact(ctx, gqlClient, encodedArtifact) + if err != nil { + t.Fatalf("Endpoint returned unexpected error: %v", err) + } + /******** check the output ********/ + var artifacts []string + for _, artifact := range resByDigest { + artifacts = append(artifacts, artifact) + } + if !cmp.Equal(artifacts, tt.expectedByDigest, cmpopts.EquateEmpty(), cmpopts.SortSlices(stdcmp.Less[string])) { + t.Errorf("RetrieveDependencies with byDigest returned %v, but wanted %v", artifacts, tt.expectedByDigest) + } + } }) } } - -func Test_DefaultLinkCondition(t *testing.T) { - /******** set up the test ********/ - ctx := logging.WithLogger(context.Background()) - gqlClient := SetupTest(t) - restApi := server.NewDefaultServer(gqlClient) - data := GuacData{ - Packages: []string{"pkg:guac/foo", "pkg:guac/bar"}, - HasSboms: []HasSbom{{ - Subject: "pkg:guac/foo", - IncludedSoftware: []string{"pkg:guac/bar"}}, - }} - Ingest(ctx, t, gqlClient, data) - - input := api.RetrieveDependenciesParams{ - Purl: ptrfrom.String("pkg:guac/foo"), - } - - /******** call the endpoint ********/ - res, err := restApi.RetrieveDependencies(ctx, api.RetrieveDependenciesRequestObject{Params: input}) - if err != nil { - t.Fatalf("RetrieveDependencies returned unexpected error: %v", err) - } - - /******** check the output ********/ - switch v := res.(type) { - case api.RetrieveDependencies200JSONResponse: - // that the default is byDigest is tested by asserting that no edges only in byName are used - if len(v.PurlList) != 0 { - t.Errorf("RetrieveDependencies returned %v, but no dependencies were expected", v) - } - default: - t.Errorf("RetrieveDependencies returned unexpected error: %v", v) - } - -} diff --git a/pkg/guacrest/server/server.go b/pkg/guacrest/server/server.go index 9582fc3c57..0e342151ea 100644 --- a/pkg/guacrest/server/server.go +++ b/pkg/guacrest/server/server.go @@ -99,3 +99,63 @@ func (s *DefaultServer) AnalyzeDependencies(ctx context.Context, request gen.Ana return nil, fmt.Errorf("%v sort is unsupported", request.Params.Sort) } } + +func (s *DefaultServer) GetPackagePurls(ctx context.Context, request gen.GetPackagePurlsRequestObject) (gen.GetPackagePurlsResponseObject, error) { + return gen.GetPackagePurls200JSONResponse{}, nil +} + +func (s *DefaultServer) GetPackageVulns(ctx context.Context, request gen.GetPackageVulnsRequestObject) (gen.GetPackageVulnsResponseObject, error) { + return gen.GetPackageVulns200JSONResponse{}, nil +} + +func (s *DefaultServer) GetPackageDeps(ctx context.Context, request gen.GetPackageDepsRequestObject) (gen.GetPackageDepsResponseObject, error) { + purls, err := GetDepsForPackage(ctx, s.gqlClient, request.Purl) + if err != nil { + err, ok := handleErr(ctx, err, GetPackageDeps).(gen.GetPackageDepsResponseObject) + if ok { + return err, nil + } else { + return gen.GetPackageDeps400JSONResponse{ + BadRequestJSONResponse: gen.BadRequestJSONResponse{ + Message: "Error handling failed", + }, + }, nil + } + } + + result := gen.GetPackageDeps200JSONResponse{} + + for _, depPurl := range purls { + result.PurlList = append(result.PurlList, depPurl) + } + + return result, nil +} + +func (s *DefaultServer) GetArtifactVulns(ctx context.Context, request gen.GetArtifactVulnsRequestObject) (gen.GetArtifactVulnsResponseObject, error) { + return gen.GetArtifactVulns200JSONResponse{}, nil +} + +func (s *DefaultServer) GetArtifactDeps(ctx context.Context, request gen.GetArtifactDepsRequestObject) (gen.GetArtifactDepsResponseObject, error) { + purls, err := GetDepsForArtifact(ctx, s.gqlClient, request.Digest) + if err != nil { + err, ok := handleErr(ctx, err, GetArtifactDeps).(gen.GetArtifactDepsResponseObject) + if ok { + return err, nil + } else { + return gen.GetArtifactDeps400JSONResponse{ + BadRequestJSONResponse: gen.BadRequestJSONResponse{ + Message: "Error handling failed", + }, + }, nil + } + } + + result := gen.GetArtifactDeps200JSONResponse{} + + for _, depPurl := range purls { + result.PurlList = append(result.PurlList, depPurl) + } + + return result, nil +}