added artifacts
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
package vulnscan
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"time"
|
||||
)
|
||||
|
||||
const defaultOSVAPI = "https://api.osv.dev/v1"
|
||||
|
||||
// Client queries the OSV (Open Source Vulnerabilities) API.
|
||||
// https://osv.dev/docs/
|
||||
type Client struct {
|
||||
baseURL string
|
||||
http *http.Client
|
||||
}
|
||||
|
||||
// NewClient creates a client that queries the public OSV API.
|
||||
func NewClient() *Client {
|
||||
return &Client{
|
||||
baseURL: defaultOSVAPI,
|
||||
http: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// QueryRequest is sent to POST /v1/query.
|
||||
type QueryRequest struct {
|
||||
Package PackageID `json:"package"`
|
||||
Version string `json:"version"`
|
||||
}
|
||||
|
||||
// PackageID identifies a package in a specific ecosystem.
|
||||
type PackageID struct {
|
||||
PURL string `json:"purl,omitempty"`
|
||||
Name string `json:"name,omitempty"`
|
||||
Ecosystem string `json:"ecosystem,omitempty"`
|
||||
}
|
||||
|
||||
// QueryResponse is the response from POST /v1/query.
|
||||
type QueryResponse struct {
|
||||
Vulns []OSVVuln `json:"vulns"`
|
||||
}
|
||||
|
||||
// OSVVuln is a vulnerability returned by the OSV API.
|
||||
type OSVVuln struct {
|
||||
ID string `json:"id"`
|
||||
Summary string `json:"summary"`
|
||||
Details string `json:"details"`
|
||||
Aliases []string `json:"aliases"`
|
||||
Fixed string `json:"fixed,omitempty"`
|
||||
Severity []Severity `json:"severity,omitempty"`
|
||||
Affected []Affected `json:"affected,omitempty"`
|
||||
Published string `json:"published,omitempty"`
|
||||
Modified string `json:"modified,omitempty"`
|
||||
}
|
||||
|
||||
// Severity holds a CVSS score from the OSV response.
|
||||
type Severity struct {
|
||||
Type string `json:"type"`
|
||||
Score string `json:"score"`
|
||||
}
|
||||
|
||||
// Affected describes a package version range.
|
||||
type Affected struct {
|
||||
Package PackageID `json:"package"`
|
||||
Ranges []AffectedRange `json:"ranges"`
|
||||
Versions []string `json:"versions"`
|
||||
}
|
||||
|
||||
type AffectedRange struct {
|
||||
Type string `json:"type"`
|
||||
Events []RangeEvent `json:"events"`
|
||||
}
|
||||
|
||||
type RangeEvent struct {
|
||||
Introduced string `json:"introduced"`
|
||||
Fixed string `json:"fixed"`
|
||||
Limit string `json:"limit"`
|
||||
}
|
||||
|
||||
// QueryByPURL queries OSV for vulnerabilities affecting a given PURL + version.
|
||||
func (c *Client) QueryByPURL(purl, version string) ([]OSVVuln, error) {
|
||||
body := QueryRequest{
|
||||
Package: PackageID{PURL: purl},
|
||||
Version: version,
|
||||
}
|
||||
return c.doQuery(body)
|
||||
}
|
||||
|
||||
// QueryByEcosystem queries OSV for vulnerabilities affecting a package in a
|
||||
// specific ecosystem (e.g. "npm", "Go", "PyPI", "cargo", "Maven", "RubyGems").
|
||||
func (c *Client) QueryByEcosystem(ecosystem, name, version string) ([]OSVVuln, error) {
|
||||
body := QueryRequest{
|
||||
Package: PackageID{
|
||||
Name: name,
|
||||
Ecosystem: ecosystem,
|
||||
},
|
||||
Version: version,
|
||||
}
|
||||
return c.doQuery(body)
|
||||
}
|
||||
|
||||
func (c *Client) doQuery(body interface{}) ([]OSVVuln, error) {
|
||||
payload, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vulnscan: marshal body: %w", err)
|
||||
}
|
||||
|
||||
req, err := http.NewRequest(http.MethodPost, c.baseURL+"/query", bytes.NewReader(payload))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vulnscan: create request: %w", err)
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := c.http.Do(req)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vulnscan: query: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
respBody, err := io.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("vulnscan: read response: %w", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, fmt.Errorf("vulnscan: OSV returned %d: %s", resp.StatusCode, string(respBody))
|
||||
}
|
||||
|
||||
var qr QueryResponse
|
||||
if err := json.Unmarshal(respBody, &qr); err != nil {
|
||||
return nil, fmt.Errorf("vulnscan: parse response: %w", err)
|
||||
}
|
||||
return qr.Vulns, nil
|
||||
}
|
||||
Reference in New Issue
Block a user