7
7
package cveclient
8
8
9
9
import (
10
+ "bytes"
10
11
"encoding/json"
11
12
"fmt"
12
13
"io"
@@ -15,6 +16,8 @@ import (
15
16
"strconv"
16
17
"strings"
17
18
"time"
19
+
20
+ "golang.org/x/vulndb/internal/cveschema5"
18
21
)
19
22
20
23
const (
@@ -128,12 +131,13 @@ func (o *ReserveOptions) getURLParams(org string) url.Values {
128
131
}
129
132
130
133
func (c * Client ) createReserveIDsRequest (opts ReserveOptions ) (* http.Request , error ) {
131
- req , err := c .createRequest (http .MethodPost , c .getURL (cveIDTarget ))
134
+ req , err := c .createRequest (http .MethodPost ,
135
+ c .getURL (cveIDTarget ), nil )
132
136
if err != nil {
133
137
return nil , err
134
138
}
135
139
req .URL .RawQuery = opts .getURLParams (c .Org ).Encode ()
136
- return req , nil
140
+ return req , err
137
141
}
138
142
139
143
type reserveIDsResponse struct {
@@ -169,16 +173,61 @@ type Quota struct {
169
173
170
174
// RetrieveQuota queries the API for the organizations reservation quota.
171
175
func (c * Client ) RetrieveQuota () (q * Quota , err error ) {
172
- err = c .queryAPI (http .MethodGet , c .getURL (orgTarget , c .Org , quotaTarget ), & q )
176
+ err = c .queryAPI (http .MethodGet , c .getURL (orgTarget , c .Org , quotaTarget ), nil , & q )
173
177
return
174
178
}
175
179
176
180
// RetrieveID requests information about an assigned CVE ID.
177
181
func (c * Client ) RetrieveID (id string ) (cve * AssignedCVE , err error ) {
178
- err = c .queryAPI (http .MethodGet , c .getURL (cveIDTarget , id ), & cve )
182
+ err = c .queryAPI (http .MethodGet , c .getURL (cveIDTarget , id ), nil , & cve )
183
+ return
184
+ }
185
+
186
+ // RetrieveRecord requests a CVE record.
187
+ func (c * Client ) RetrieveRecord (id string ) (cve * cveschema5.CVERecord , err error ) {
188
+ err = c .queryAPI (http .MethodGet , c .getURL (cveTarget , id , cnaTarget ), nil , & cve )
179
189
return
180
190
}
181
191
192
+ func (c * Client ) getCVERecordEndpoint (cveID string ) string {
193
+ return c .getURL (cveTarget , cveID , cnaTarget )
194
+ }
195
+
196
+ type recordRequestBody struct {
197
+ CNAContainer cveschema5.CNAPublishedContainer `json:"cnaContainer"`
198
+ }
199
+ type createResponse struct {
200
+ Created cveschema5.CVERecord `json:"created"`
201
+ }
202
+
203
+ func (c * Client ) CreateRecord (id string , record * cveschema5.Containers ) (* cveschema5.CVERecord , error ) {
204
+ requestBody := recordRequestBody {
205
+ CNAContainer : record .CNAContainer ,
206
+ }
207
+ var response createResponse
208
+ err := c .queryAPI (http .MethodPost , c .getCVERecordEndpoint (id ), requestBody , & response )
209
+ if err != nil {
210
+ return nil , err
211
+ }
212
+ return & response .Created , nil
213
+ }
214
+
215
+ type updateResponse struct {
216
+ Updated cveschema5.CVERecord `json:"updated"`
217
+ }
218
+
219
+ func (c * Client ) UpdateRecord (id string , record * cveschema5.Containers ) (* cveschema5.CVERecord , error ) {
220
+ requestBody := recordRequestBody {
221
+ CNAContainer : record .CNAContainer ,
222
+ }
223
+ var response updateResponse
224
+ err := c .queryAPI (http .MethodPut , c .getCVERecordEndpoint (id ), requestBody , & response )
225
+ if err != nil {
226
+ return nil , err
227
+ }
228
+ return & response .Updated , nil
229
+ }
230
+
182
231
type Org struct {
183
232
Name string `json:"name"`
184
233
ShortName string `json:"short_name"`
@@ -187,7 +236,7 @@ type Org struct {
187
236
188
237
// RetrieveOrg requests information about an organization.
189
238
func (c * Client ) RetrieveOrg () (org * Org , err error ) {
190
- err = c .queryAPI (http .MethodGet , c .getURL (orgTarget , c .Org ), & org )
239
+ err = c .queryAPI (http .MethodGet , c .getURL (orgTarget , c .Org ), nil , & org )
191
240
return
192
241
}
193
242
@@ -257,8 +306,8 @@ type listOrgCVEsResponse struct {
257
306
CVEs AssignedCVEList `json:"cve_ids"`
258
307
}
259
308
260
- func (c Client ) createListOrgCVEsRequest (opts * ListOptions , page int ) (* http.Request , error ) {
261
- req , err : = c .createRequest (http .MethodGet , c .getURL (cveIDTarget ))
309
+ func (c Client ) createListOrgCVEsRequest (opts * ListOptions , page int ) (req * http.Request , err error ) {
310
+ req , err = c .createRequest (http .MethodGet , c .getURL (cveIDTarget ), nil )
262
311
if err != nil {
263
312
return nil , err
264
313
}
@@ -267,7 +316,7 @@ func (c Client) createListOrgCVEsRequest(opts *ListOptions, page int) (*http.Req
267
316
params .Set ("page" , fmt .Sprint (page ))
268
317
}
269
318
req .URL .RawQuery = params .Encode ()
270
- return req , nil
319
+ return
271
320
}
272
321
273
322
// ListOrgCVEs requests information about the CVEs the organization has been
@@ -294,8 +343,8 @@ func (c *Client) ListOrgCVEs(opts *ListOptions) (AssignedCVEList, error) {
294
343
return cves , nil
295
344
}
296
345
297
- func (c * Client ) queryAPI (method , url string , response any ) error {
298
- req , err := c .createRequest (method , url )
346
+ func (c * Client ) queryAPI (method , url string , requestBody any , response any ) error {
347
+ req , err := c .createRequest (method , url , requestBody )
299
348
if err != nil {
300
349
return err
301
350
}
@@ -313,14 +362,23 @@ var (
313
362
)
314
363
315
364
// createRequest creates a new HTTP request and sets the header fields.
316
- func (c * Client ) createRequest (method , url string ) (* http.Request , error ) {
317
- req , err := http .NewRequest (method , url , nil )
365
+ func (c * Client ) createRequest (method , url string , body any ) (* http.Request , error ) {
366
+ var r io.Reader
367
+ if body != nil {
368
+ b , err := json .Marshal (body )
369
+ if err != nil {
370
+ return nil , err
371
+ }
372
+ r = bytes .NewReader (b )
373
+ }
374
+ req , err := http .NewRequest (method , url , r )
318
375
if err != nil {
319
376
return nil , err
320
377
}
321
378
req .Header .Set (headerApiUser , c .User )
322
379
req .Header .Set (headerApiOrg , c .Org )
323
380
req .Header .Set (headerApiKey , c .Key )
381
+ req .Header .Set ("Content-Type" , "application/json" )
324
382
return req , nil
325
383
}
326
384
@@ -352,18 +410,30 @@ func (c *Client) sendRequest(req *http.Request, checkStatus func(int) bool, resu
352
410
}
353
411
354
412
var (
413
+ cveTarget = "cve"
355
414
cveIDTarget = "cve-id"
356
415
orgTarget = "org"
357
416
quotaTarget = "id_quota"
417
+ cnaTarget = "cna"
358
418
)
359
419
360
420
func (c * Client ) getURL (targets ... string ) string {
361
421
return fmt .Sprintf ("%s/api/%s" , c .Endpoint , strings .Join (targets , "/" ))
362
422
}
363
423
364
424
type apiError struct {
365
- Error string `json:"error"`
366
- Message string `json:"message"`
425
+ Error string `json:"error"`
426
+ Message string `json:"message"`
427
+ Detail apiErrorDetail `json:"details"`
428
+ }
429
+
430
+ type apiErrorDetail struct {
431
+ Errors []apiErrorInner `json:"errors"`
432
+ }
433
+
434
+ type apiErrorInner struct {
435
+ InstancePath string `json:"instancePath"`
436
+ Message string `json:"message"`
367
437
}
368
438
369
439
// extractError extracts additional error messages from the HTTP response
@@ -372,13 +442,11 @@ func extractError(resp *http.Response) error {
372
442
errMsg := resp .Status
373
443
body , err := io .ReadAll (resp .Body )
374
444
if err != nil {
375
- // Discard the read error and return the HTTP status.
376
- return fmt .Errorf (errMsg )
445
+ return fmt .Errorf ("%s: could not read error data: %s" , errMsg , err )
377
446
}
378
447
var apiErr apiError
379
448
if err := json .Unmarshal (body , & apiErr ); err != nil {
380
- // Discard the unmarshal error and return the HTTP status.
381
- return fmt .Errorf (errMsg )
449
+ return fmt .Errorf ("%s: could not unmarshal error: %s" , errMsg , err )
382
450
}
383
451
384
452
// Append the error and message text if they add extra information
@@ -389,5 +457,10 @@ func extractError(resp *http.Response) error {
389
457
errMsg = fmt .Sprintf ("%s: %s" , errMsg , errText )
390
458
}
391
459
}
460
+
461
+ for _ , detail := range apiErr .Detail .Errors {
462
+ errMsg = fmt .Sprintf ("%s\n %s: %s" , errMsg , detail .InstancePath , detail .Message )
463
+ }
464
+
392
465
return fmt .Errorf (errMsg )
393
466
}
0 commit comments