New API: list vulnerabilities by namespace
Signed-off-by: liangchenye <liangchenye@huawei.com>
This commit is contained in:
parent
ee28073d61
commit
a541e964e0
@ -8,6 +8,7 @@
|
||||
- [Namespaces](#namespaces)
|
||||
- [GET](#get-namespaces)
|
||||
- [Vulnerabilities](#vulnerabilities)
|
||||
- [List](#get-namespacesnsnamevulnerabilities)
|
||||
- [POST](#post-namespacesnamevulnerabilities)
|
||||
- [GET](#get-namespacesnsnamevulnerabilitiesvulnname)
|
||||
- [PUT](#put-namespacesnsnamevulnerabilitiesvulnname)
|
||||
@ -196,6 +197,60 @@ Server: clair
|
||||
|
||||
## Vulnerabilities
|
||||
|
||||
#### GET /namespaces/`:nsName`/vulnerabilities
|
||||
|
||||
###### Description
|
||||
|
||||
The GET route for the Vulnerabilities resource displays the vulnerabilities data for a given namespace.
|
||||
|
||||
###### Query Parameters
|
||||
|
||||
| Name | Type | Required | Description |
|
||||
|---------|------|----------|------------------------------------------------------------|
|
||||
| limit | int | required | Limits the amount of the vunlerabilities data for a given namespace. |
|
||||
| page | int | required | Displays the specific page of the vunlerabilities data for a given namespace. |
|
||||
|
||||
###### Example Request
|
||||
|
||||
```json
|
||||
GET http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities?page=0&limit=2 HTTP/1.1
|
||||
```
|
||||
|
||||
###### Example Response
|
||||
|
||||
```json
|
||||
HTTP/1.1 200 OK
|
||||
Content-Type: application/json;charset=utf-8
|
||||
Server: clair
|
||||
|
||||
{
|
||||
"Vulnerabilities": [
|
||||
{
|
||||
"Name": "CVE-1999-1332",
|
||||
"Namespace": "debian:8",
|
||||
"Description": "gzexe in the gzip package on Red Hat Linux 5.0 and earlier allows local users to overwrite files of other users via a symlink attack on a temporary file.",
|
||||
"Link": "https://security-tracker.debian.org/tracker/CVE-1999-1332",
|
||||
"Severity": "Low"
|
||||
},
|
||||
{
|
||||
"Name": "CVE-1999-1572",
|
||||
"Namespace": "debian:8",
|
||||
"Description": "cpio on FreeBSD 2.1.0, Debian GNU/Linux 3.0, and possibly other operating systems, uses a 0 umask when creating files using the -O (archive) or -F options, which creates the files with mode 0666 and allows local users to read or overwrite those files.",
|
||||
"Link": "https://security-tracker.debian.org/tracker/CVE-1999-1572",
|
||||
"Severity": "Low",
|
||||
"Metadata": {
|
||||
"NVD": {
|
||||
"CVSSv2": {
|
||||
"Score": 2.1,
|
||||
"Vectors": "AV:L/AC:L/Au:N/C:P/I:N"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### POST /namespaces/`:name`/vulnerabilities
|
||||
|
||||
###### Description
|
||||
|
@ -275,6 +275,7 @@ type NamespaceEnvelope struct {
|
||||
|
||||
type VulnerabilityEnvelope struct {
|
||||
Vulnerability *Vulnerability `json:"Vulnerability,omitempty"`
|
||||
Vulnerabilities *[]Vulnerability `json:"Vulnerabilities,omitempty"`
|
||||
Error *Error `json:"Error,omitempty"`
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ func NewRouter(ctx *context.RouteContext) *httprouter.Router {
|
||||
router.GET("/namespaces", context.HTTPHandler(getNamespaces, ctx))
|
||||
|
||||
// Vulnerabilities
|
||||
router.GET("/namespaces/:namespaceName/vulnerabilities", context.HTTPHandler(getVulnerabilities, ctx))
|
||||
router.POST("/namespaces/:namespaceName/vulnerabilities", context.HTTPHandler(postVulnerability, ctx))
|
||||
router.GET("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", context.HTTPHandler(getVulnerability, ctx))
|
||||
router.PUT("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", context.HTTPHandler(putVulnerability, ctx))
|
||||
|
@ -38,6 +38,7 @@ const (
|
||||
getLayerRoute = "v1/getLayer"
|
||||
deleteLayerRoute = "v1/deleteLayer"
|
||||
getNamespacesRoute = "v1/getNamespaces"
|
||||
getVulnerabilitiesRoute = "v1/getVulnerabilities"
|
||||
postVulnerabilityRoute = "v1/postVulnerability"
|
||||
getVulnerabilityRoute = "v1/getVulnerability"
|
||||
putVulnerabilityRoute = "v1/putVulnerability"
|
||||
@ -184,6 +185,53 @@ func getNamespaces(w http.ResponseWriter, r *http.Request, p httprouter.Params,
|
||||
return getNamespacesRoute, http.StatusOK
|
||||
}
|
||||
|
||||
func getVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
||||
query := r.URL.Query()
|
||||
|
||||
limitStrs, limitExists := query["limit"]
|
||||
if !limitExists {
|
||||
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"must provide limit query parameter"}})
|
||||
return getVulnerabilitiesRoute, http.StatusBadRequest
|
||||
}
|
||||
limit, err := strconv.Atoi(limitStrs[0])
|
||||
if err != nil {
|
||||
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"invalid limit format: " + err.Error()}})
|
||||
return getVulnerabilitiesRoute, http.StatusBadRequest
|
||||
} else if limit <= 0 {
|
||||
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"limit value should not be less than zero"}})
|
||||
return getVulnerabilitiesRoute, http.StatusBadRequest
|
||||
}
|
||||
|
||||
pageStrs, pageExists := query["page"]
|
||||
if !pageExists {
|
||||
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"must provide page query parameter"}})
|
||||
return getVulnerabilitiesRoute, http.StatusBadRequest
|
||||
}
|
||||
page, err := strconv.Atoi(pageStrs[0])
|
||||
if err != nil {
|
||||
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"invalid page format: " + err.Error()}})
|
||||
return getVulnerabilitiesRoute, http.StatusBadRequest
|
||||
} else if page < 0 {
|
||||
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"page value should not be less than zero"}})
|
||||
return getVulnerabilitiesRoute, http.StatusBadRequest
|
||||
}
|
||||
|
||||
dbVulns, err := ctx.Store.ListVulnerabilities(p.ByName("namespaceName"), limit, page)
|
||||
if err != nil {
|
||||
writeResponse(w, r, http.StatusInternalServerError, VulnerabilityEnvelope{Error: &Error{err.Error()}})
|
||||
return getVulnerabilitiesRoute, http.StatusInternalServerError
|
||||
}
|
||||
|
||||
var vulns []Vulnerability
|
||||
for _, dbVuln := range dbVulns {
|
||||
vuln := VulnerabilityFromDatabaseModel(dbVuln, false)
|
||||
vulns = append(vulns, vuln)
|
||||
}
|
||||
|
||||
writeResponse(w, r, http.StatusOK, VulnerabilityEnvelope{Vulnerabilities: &vulns})
|
||||
return getVulnerabilitiesRoute, http.StatusOK
|
||||
}
|
||||
|
||||
func postVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
|
||||
request := VulnerabilityEnvelope{}
|
||||
err := decodeJSON(r, &request)
|
||||
|
@ -60,6 +60,10 @@ type Datastore interface {
|
||||
DeleteLayer(name string) error
|
||||
|
||||
// # Vulnerability
|
||||
// ListVulnerabilities returns the list of vulnerabilies of a certain Namespace.
|
||||
// The Limit and page parameters are used to paginate the return list.
|
||||
ListVulnerabilities(namespaceName string, limit int, page int) ([]Vulnerability, error)
|
||||
|
||||
// InsertVulnerabilities stores the given Vulnerabilities in the database, updating them if
|
||||
// necessary. A vulnerability is uniquely identified by its Namespace and its Name.
|
||||
// The FixedIn field may only contain a partial list of Features that are affected by the
|
||||
|
@ -144,6 +144,9 @@ const (
|
||||
searchVulnerabilityForUpdate = ` FOR UPDATE OF v`
|
||||
searchVulnerabilityByNamespaceAndName = ` WHERE n.name = $1 AND v.name = $2 AND v.deleted_at IS NULL`
|
||||
searchVulnerabilityByID = ` WHERE v.id = $1`
|
||||
searchVulnerabilityByNamespace = ` WHERE n.name = $1 AND v.deleted_at IS NULL
|
||||
ORDER BY v.name
|
||||
LIMIT $2 offset $3`
|
||||
|
||||
searchVulnerabilityFixedIn = `
|
||||
SELECT vfif.version, f.id, f.Name
|
||||
|
@ -28,6 +28,50 @@ import (
|
||||
"github.com/guregu/null/zero"
|
||||
)
|
||||
|
||||
func (pgSQL *pgSQL) ListVulnerabilities(namespaceName string, limit int, page int) ([]database.Vulnerability, error) {
|
||||
return listVulnerabilities(pgSQL, namespaceName, limit, page)
|
||||
}
|
||||
|
||||
func listVulnerabilities(queryer Queryer, namespaceName string, limit int, page int) ([]database.Vulnerability, error) {
|
||||
defer observeQueryTime("listVulnerabilities", "all", time.Now())
|
||||
|
||||
// Query.
|
||||
query := searchVulnerabilityBase + searchVulnerabilityByNamespace
|
||||
rows, err := queryer.Query(query, namespaceName, limit, page*limit)
|
||||
if err != nil {
|
||||
return nil, handleError("searchVulnerabilityByNamespace", err)
|
||||
}
|
||||
defer rows.Close()
|
||||
|
||||
var vulns []database.Vulnerability
|
||||
// Scan query.
|
||||
for rows.Next() {
|
||||
var vulnerability database.Vulnerability
|
||||
|
||||
err := rows.Scan(
|
||||
&vulnerability.ID,
|
||||
&vulnerability.Name,
|
||||
&vulnerability.Namespace.ID,
|
||||
&vulnerability.Namespace.Name,
|
||||
&vulnerability.Description,
|
||||
&vulnerability.Link,
|
||||
&vulnerability.Severity,
|
||||
&vulnerability.Metadata,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, handleError("searchVulnerabilityByNamespace.Scan()", err)
|
||||
} else {
|
||||
vulns = append(vulns, vulnerability)
|
||||
}
|
||||
}
|
||||
|
||||
if err := rows.Err(); err != nil {
|
||||
return nil, handleError("searchVulnerabilityByNamespace.Rows()", err)
|
||||
}
|
||||
|
||||
return vulns, nil
|
||||
}
|
||||
|
||||
func (pgSQL *pgSQL) FindVulnerability(namespaceName, name string) (database.Vulnerability, error) {
|
||||
return findVulnerability(pgSQL, namespaceName, name, false)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user