зеркало из https://github.com/golang/vulndb.git
x/vulndb: add cve org command to lookup org info
Also adds a minor refactor of cveclient to increase code reuse. Change-Id: I67e798e35124913d916d743f86dcbbbc8d7a6b37 Reviewed-on: https://go-review.googlesource.com/c/vulndb/+/412877 Reviewed-by: Damien Neil <dneil@google.com> TryBot-Result: Gopher Robot <gobot@golang.org> Run-TryBot: Tatiana Bradley <tatiana@golang.org> Reviewed-by: Tatiana Bradley <tatiana@golang.org>
This commit is contained in:
Родитель
c71e66c2a9
Коммит
e5430e2fed
|
@ -45,8 +45,9 @@ func main() {
|
|||
formatCmd := " %s: %s\n"
|
||||
fmt.Fprintf(out, "usage: cve [-key] [-user] [-org] [-test] <cmd> ...\n commands:\n")
|
||||
fmt.Fprintf(out, formatCmd, "[-n] [-seq] [-year] reserve", "reserves new CVE IDs")
|
||||
fmt.Fprintf(out, formatCmd, "quota", "outputs the CVE ID quota of an organization")
|
||||
fmt.Fprintf(out, formatCmd, "lookup {cve-id}", "outputs details on an assigned CVE ID (CVE-YYYY-NNNN)")
|
||||
fmt.Fprintf(out, formatCmd, "quota", "outputs the CVE ID quota of the authenticated organization")
|
||||
fmt.Fprintf(out, formatCmd, "id {cve-id}", "outputs details on an assigned CVE ID (CVE-YYYY-NNNN)")
|
||||
fmt.Fprintf(out, formatCmd, "org", "outputs details on the authenticated organization")
|
||||
fmt.Fprintf(out, formatCmd, "[-year] [-state] list", "lists all CVE IDs for an organization")
|
||||
flag.PrintDefaults()
|
||||
}
|
||||
|
@ -102,13 +103,17 @@ func main() {
|
|||
if err := quota(c); err != nil {
|
||||
log.Fatalf("cve quota: could not retrieve quota info due to error:\n %v", err)
|
||||
}
|
||||
case "lookup":
|
||||
case "id":
|
||||
id, err := validateID(flag.Arg(1))
|
||||
if err != nil {
|
||||
logUsageErr("cve lookup", err)
|
||||
logUsageErr("cve id", err)
|
||||
}
|
||||
if err := lookup(c, id); err != nil {
|
||||
log.Fatalf("cve lookup: could not retrieve CVE IDs due to error:\n %v", err)
|
||||
if err := lookupID(c, id); err != nil {
|
||||
log.Fatalf("cve id: could not retrieve CVE IDs due to error:\n %v", err)
|
||||
}
|
||||
case "org":
|
||||
if err := lookupOrg(c); err != nil {
|
||||
log.Fatalf("cve org: could not retrieve org info due to error:\n %v", err)
|
||||
}
|
||||
case "list":
|
||||
// TODO(http://go.dev/issues/53258): allow time-based filters via flags.
|
||||
|
@ -185,8 +190,17 @@ func quota(c *cveclient.Client) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
func lookup(c *cveclient.Client, id string) error {
|
||||
cve, err := c.RetrieveCVE(id)
|
||||
func lookupOrg(c *cveclient.Client) error {
|
||||
org, err := c.RetrieveOrg()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fmt.Printf("org name: %q\nshort name: %q\nuuid: %s\n", org.Name, org.ShortName, org.UUID)
|
||||
return nil
|
||||
}
|
||||
|
||||
func lookupID(c *cveclient.Client, id string) error {
|
||||
cve, err := c.RetrieveID(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
|
|
@ -128,8 +128,7 @@ func (o *ReserveOptions) getURLParams(org string) url.Values {
|
|||
}
|
||||
|
||||
func (c *Client) createReserveIDsRequest(opts ReserveOptions) (*http.Request, error) {
|
||||
req, err := c.createRequest(http.MethodPost,
|
||||
fmt.Sprintf("%s/api/cve-id", c.Endpoint))
|
||||
req, err := c.createRequest(http.MethodPost, c.getURL(cveIDTarget))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -169,33 +168,27 @@ type Quota struct {
|
|||
}
|
||||
|
||||
// RetrieveQuota queries the API for the organizations reservation quota.
|
||||
func (c *Client) RetrieveQuota() (Quota, error) {
|
||||
req, err := c.createRequest(http.MethodGet, fmt.Sprintf("%s/api/org/%s/id_quota", c.Endpoint, c.Org))
|
||||
if err != nil {
|
||||
return Quota{}, err
|
||||
}
|
||||
|
||||
var q Quota
|
||||
err = c.sendRequest(req, nil, &q)
|
||||
if err != nil {
|
||||
return Quota{}, err
|
||||
}
|
||||
return q, nil
|
||||
func (c *Client) RetrieveQuota() (q *Quota, err error) {
|
||||
err = c.queryAPI(http.MethodGet, c.getURL(orgTarget, c.Org, quotaTarget), &q)
|
||||
return
|
||||
}
|
||||
|
||||
// RetrieveCVE requests information about an assigned CVE ID.
|
||||
func (c *Client) RetrieveCVE(id string) (AssignedCVE, error) {
|
||||
req, err := c.createRequest(http.MethodGet, fmt.Sprintf("%s/api/cve-id/%s", c.Endpoint, id))
|
||||
if err != nil {
|
||||
return AssignedCVE{}, err
|
||||
}
|
||||
// RetrieveID requests information about an assigned CVE ID.
|
||||
func (c *Client) RetrieveID(id string) (cve *AssignedCVE, err error) {
|
||||
err = c.queryAPI(http.MethodGet, c.getURL(cveIDTarget, id), &cve)
|
||||
return
|
||||
}
|
||||
|
||||
var cve AssignedCVE
|
||||
err = c.sendRequest(req, nil, &cve)
|
||||
if err != nil {
|
||||
return AssignedCVE{}, err
|
||||
}
|
||||
return cve, nil
|
||||
type Org struct {
|
||||
Name string `json:"name"`
|
||||
ShortName string `json:"short_name"`
|
||||
UUID string `json:"UUID"`
|
||||
}
|
||||
|
||||
// RetrieveOrg requests information about an organization.
|
||||
func (c *Client) RetrieveOrg() (org *Org, err error) {
|
||||
err = c.queryAPI(http.MethodGet, c.getURL(orgTarget, c.Org), &org)
|
||||
return
|
||||
}
|
||||
|
||||
// ListOptions contains filters to be used when requesting a list of
|
||||
|
@ -234,6 +227,9 @@ func (o ListOptions) String() string {
|
|||
|
||||
func (o *ListOptions) getURLParams() url.Values {
|
||||
params := url.Values{}
|
||||
if o == nil {
|
||||
return params
|
||||
}
|
||||
if o.State != "" {
|
||||
params.Set("state", o.State)
|
||||
}
|
||||
|
@ -262,14 +258,11 @@ type listOrgCVEsResponse struct {
|
|||
}
|
||||
|
||||
func (c Client) createListOrgCVEsRequest(opts *ListOptions, page int) (*http.Request, error) {
|
||||
req, err := c.createRequest(http.MethodGet, fmt.Sprintf("%s/api/cve-id", c.Endpoint))
|
||||
req, err := c.createRequest(http.MethodGet, c.getURL(cveIDTarget))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := url.Values{}
|
||||
if opts != nil {
|
||||
params = opts.getURLParams()
|
||||
}
|
||||
params := opts.getURLParams()
|
||||
if page > 0 {
|
||||
params.Set("page", fmt.Sprint(page))
|
||||
}
|
||||
|
@ -301,6 +294,18 @@ func (c *Client) ListOrgCVEs(opts *ListOptions) (AssignedCVEList, error) {
|
|||
return cves, nil
|
||||
}
|
||||
|
||||
func (c *Client) queryAPI(method, url string, response any) error {
|
||||
req, err := c.createRequest(method, url)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = c.sendRequest(req, nil, response)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
headerApiUser = "CVE-API-USER"
|
||||
headerApiOrg = "CVE-API-ORG"
|
||||
|
@ -334,7 +339,7 @@ func (c *Client) sendRequest(req *http.Request, checkStatus func(int) bool, resu
|
|||
}
|
||||
}
|
||||
if !checkStatus(resp.StatusCode) {
|
||||
return fmt.Errorf("HTTP request %s %q returned error: %w", req.Method, req.URL, extractError(resp))
|
||||
return fmt.Errorf("HTTP request %s %q returned error: %v", req.Method, req.URL, extractError(resp))
|
||||
}
|
||||
body, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
|
@ -346,6 +351,16 @@ func (c *Client) sendRequest(req *http.Request, checkStatus func(int) bool, resu
|
|||
return nil
|
||||
}
|
||||
|
||||
var (
|
||||
cveIDTarget = "cve-id"
|
||||
orgTarget = "org"
|
||||
quotaTarget = "id_quota"
|
||||
)
|
||||
|
||||
func (c *Client) getURL(targets ...string) string {
|
||||
return fmt.Sprintf("%s/api/%s", c.Endpoint, strings.Join(targets, "/"))
|
||||
}
|
||||
|
||||
type apiError struct {
|
||||
Error string `json:"error"`
|
||||
Message string `json:"message"`
|
||||
|
|
|
@ -29,11 +29,16 @@ var defaultTestCVE = newTestCVE("CVE-2022-0000", cveschema.StateReserved, "2022"
|
|||
var defaultTestCVEs = AssignedCVEList{
|
||||
defaultTestCVE, newTestCVE("CVE-2022-0001", cveschema.StateReserved, "2022"),
|
||||
}
|
||||
var defaultTestQuota = Quota{
|
||||
var defaultTestQuota = &Quota{
|
||||
Quota: 10,
|
||||
Reserved: 3,
|
||||
Available: 7,
|
||||
}
|
||||
var defaultTestOrg = &Org{
|
||||
Name: "An Org",
|
||||
ShortName: testApiOrg,
|
||||
UUID: "000-000-000",
|
||||
}
|
||||
|
||||
var (
|
||||
testTime2022 = time.Date(2022, 1, 1, 0, 0, 0, 0, time.UTC)
|
||||
|
@ -188,8 +193,11 @@ var (
|
|||
retrieveQuotaQuery = func(c *Client) (any, error) {
|
||||
return c.RetrieveQuota()
|
||||
}
|
||||
retrieveCVEQuery = func(c *Client) (any, error) {
|
||||
return c.RetrieveCVE(defaultTestCVE.ID)
|
||||
retrieveIDQuery = func(c *Client) (any, error) {
|
||||
return c.RetrieveID(defaultTestCVE.ID)
|
||||
}
|
||||
retrieveOrgQuery = func(c *Client) (any, error) {
|
||||
return c.RetrieveOrg()
|
||||
}
|
||||
listOrgCVEsQuery = func(c *Client) (any, error) {
|
||||
return c.ListOrgCVEs(&ListOptions{})
|
||||
|
@ -236,13 +244,22 @@ func TestAllSuccess(t *testing.T) {
|
|||
want: defaultTestQuota,
|
||||
},
|
||||
{
|
||||
name: "RetrieveCVE",
|
||||
query: retrieveCVEQuery,
|
||||
name: "RetrieveID",
|
||||
query: retrieveIDQuery,
|
||||
mockStatus: http.StatusOK,
|
||||
mockResponse: defaultTestCVE,
|
||||
wantHTTPMethod: http.MethodGet,
|
||||
wantPath: "/api/cve-id/CVE-2022-0000",
|
||||
want: defaultTestCVE,
|
||||
want: &defaultTestCVE,
|
||||
},
|
||||
{
|
||||
name: "RetrieveOrg",
|
||||
query: retrieveOrgQuery,
|
||||
mockStatus: http.StatusOK,
|
||||
mockResponse: defaultTestOrg,
|
||||
wantHTTPMethod: http.MethodGet,
|
||||
wantPath: "/api/org/test_api_org",
|
||||
want: defaultTestOrg,
|
||||
},
|
||||
{
|
||||
name: "ListOrgCVEs/single page",
|
||||
|
@ -296,8 +313,12 @@ func TestAllFail(t *testing.T) {
|
|||
query: retrieveQuotaQuery,
|
||||
},
|
||||
{
|
||||
name: "RetrieveCVE",
|
||||
query: retrieveCVEQuery,
|
||||
name: "RetrieveID",
|
||||
query: retrieveIDQuery,
|
||||
},
|
||||
{
|
||||
name: "RetrieveOrg",
|
||||
query: retrieveOrgQuery,
|
||||
},
|
||||
{
|
||||
name: "ListOrgCVEs",
|
||||
|
|
Загрузка…
Ссылка в новой задаче