update api, update testcases

Signed-off-by: liang chenye <liangchenye@huawei.com>
This commit is contained in:
liang chenye 2016-05-30 17:31:25 +08:00
parent 0a997145ed
commit 3c3a96e71d
25 changed files with 359 additions and 227 deletions

View File

@ -129,18 +129,18 @@ Server: clair
{ {
"Layer": { "Layer": {
"Name": "17675ec01494d651e1ccf81dc9cf63959ebfeed4f978fddb1666b6ead008ed52", "Name": "17675ec01494d651e1ccf81dc9cf63959ebfeed4f978fddb1666b6ead008ed52",
"NamespaceName": "debian:8", "Namespaces": [{"Name": "debian", "Version": "8"}],
"ParentName": "140f9bdfeb9784cf8730e9dab5dd12fbd704151cf555ac8cae650451794e5ac2", "ParentName": "140f9bdfeb9784cf8730e9dab5dd12fbd704151cf555ac8cae650451794e5ac2",
"IndexedByVersion": 1, "IndexedByVersion": 1,
"Features": [ "Features": [
{ {
"Name": "coreutils", "Name": "coreutils",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Version": "8.23-4", "Version": "8.23-4",
"Vulnerabilities": [ "Vulnerabilities": [
{ {
"Name": "CVE-2014-9471", "Name": "CVE-2014-9471",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.", "Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.",
"Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471", "Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471",
"Severity": "Low", "Severity": "Low",
@ -196,15 +196,15 @@ Server: clair
{ {
"Namespaces": [ "Namespaces": [
{ "Name": "debian:8" }, { "ID": "gAAAAABXTQKgma_TLKq0wr1D6wVB507N3fi9wsWMUypYOSXTDVxQ8OR5L5S6PqZ9Wh0IzWojnVmlspyTL4cyjytyra7U9vAHMA==", "Name": "debian", "Version": "8" },
{ "Name": "debian:9" } { "ID": "gAAAAABXTQPZmOFlOR8zzuhv8Y2fD7HbUY8O6z_Py2vibB9uveWZoycSY1HDIkcf7lN_UDynom4kWubFS4h9KBCbWwjNIqacsw==", "Name": "debian", "Version": "9" }
] ]
} }
``` ```
## Vulnerabilities ## Vulnerabilities
#### GET /namespaces/`:nsName`/vulnerabilities #### GET /namespaces/`:nsID`/vulnerabilities
###### Description ###### Description
@ -220,7 +220,7 @@ The GET route for the Vulnerabilities resource displays the vulnerabilities data
###### Example Request ###### Example Request
```json ```json
GET http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities?limit=2 HTTP/1.1 GET http://localhost:6060/v1/namespaces/gAAAAABXTQKgma_TLKq0wr1D6wVB507N3fi9wsWMUypYOSXTDVxQ8OR5L5S6PqZ9Wh0IzWojnVmlspyTL4cyjytyra7U9vAHMA==/vulnerabilities?limit=2 HTTP/1.1
``` ```
###### Example Response ###### Example Response
@ -234,14 +234,14 @@ Server: clair
"Vulnerabilities": [ "Vulnerabilities": [
{ {
"Name": "CVE-1999-1332", "Name": "CVE-1999-1332",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "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.", "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", "Link": "https://security-tracker.debian.org/tracker/CVE-1999-1332",
"Severity": "Low" "Severity": "Low"
}, },
{ {
"Name": "CVE-1999-1572", "Name": "CVE-1999-1572",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "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.", "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", "Link": "https://security-tracker.debian.org/tracker/CVE-1999-1572",
"Severity": "Low", "Severity": "Low",
@ -259,7 +259,7 @@ Server: clair
} }
``` ```
#### POST /namespaces/`:name`/vulnerabilities #### POST /namespaces/`:nsID`/vulnerabilities
###### Description ###### Description
@ -268,12 +268,12 @@ The POST route for the Vulnerabilities resource creates a new Vulnerability.
###### Example Request ###### Example Request
```json ```json
POST http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities HTTP/1.1 POST http://localhost:6060/v1/namespaces/gAAAAABXTQKgma_TLKq0wr1D6wVB507N3fi9wsWMUypYOSXTDVxQ8OR5L5S6PqZ9Wh0IzWojnVmlspyTL4cyjytyra7U9vAHMA==/vulnerabilities HTTP/1.1
{ {
"Vulnerability": { "Vulnerability": {
"Name": "CVE-2014-9471", "Name": "CVE-2014-9471",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471", "Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471",
"Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.", "Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.",
"Severity": "Low", "Severity": "Low",
@ -288,7 +288,7 @@ POST http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities HTTP/1.1
"FixedIn": [ "FixedIn": [
{ {
"Name": "coreutils", "Name": "coreutils",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Version": "8.23-1" "Version": "8.23-1"
} }
] ]
@ -306,7 +306,7 @@ Server: clair
{ {
"Vulnerability": { "Vulnerability": {
"Name": "CVE-2014-9471", "Name": "CVE-2014-9471",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471", "Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471",
"Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.", "Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.",
"Severity": "Low", "Severity": "Low",
@ -321,7 +321,7 @@ Server: clair
"FixedIn": [ "FixedIn": [
{ {
"Name": "coreutils", "Name": "coreutils",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Version": "8.23-1" "Version": "8.23-1"
} }
] ]
@ -329,7 +329,7 @@ Server: clair
} }
``` ```
#### GET /namespaces/`:nsName`/vulnerabilities/`:vulnName` #### GET /namespaces/`:nsID`/vulnerabilities/`:vulnName`
###### Description ###### Description
@ -344,7 +344,7 @@ The GET route for the Vulnerabilities resource displays the current data for a g
###### Example Request ###### Example Request
```json ```json
GET http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities/CVE-2014-9471?fixedIn HTTP/1.1 GET http://localhost:6060/v1/namespaces/gAAAAABXTQKgma_TLKq0wr1D6wVB507N3fi9wsWMUypYOSXTDVxQ8OR5L5S6PqZ9Wh0IzWojnVmlspyTL4cyjytyra7U9vAHMA==/vulnerabilities/CVE-2014-9471?fixedIn HTTP/1.1
``` ```
###### Example Response ###### Example Response
@ -357,7 +357,7 @@ Server: clair
{ {
"Vulnerability": { "Vulnerability": {
"Name": "CVE-2014-9471", "Name": "CVE-2014-9471",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471", "Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471",
"Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.", "Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.",
"Severity": "Low", "Severity": "Low",
@ -372,7 +372,7 @@ Server: clair
"FixedIn": [ "FixedIn": [
{ {
"Name": "coreutils", "Name": "coreutils",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Version": "8.23-1" "Version": "8.23-1"
} }
] ]
@ -380,7 +380,7 @@ Server: clair
} }
``` ```
#### PUT /namespaces/`:nsName`/vulnerabilities/`:vulnName` #### PUT /namespaces/`:nsID`/vulnerabilities/`:vulnName`
###### Description ###### Description
@ -392,12 +392,12 @@ If this vulnerability was inserted by a Fetcher, changes may be lost when the Fe
###### Example Request ###### Example Request
```json ```json
PUT http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities/CVE-2014-9471 PUT http://localhost:6060/v1/namespaces/gAAAAABXTQKgma_TLKq0wr1D6wVB507N3fi9wsWMUypYOSXTDVxQ8OR5L5S6PqZ9Wh0IzWojnVmlspyTL4cyjytyra7U9vAHMA==/vulnerabilities/CVE-2014-9471
{ {
"Vulnerability": { "Vulnerability": {
"Name": "CVE-2014-9471", "Name": "CVE-2014-9471",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471", "Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471",
"Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.", "Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.",
"Severity": "Low", "Severity": "Low",
@ -422,7 +422,7 @@ Server: clair
{ {
"Vulnerability": { "Vulnerability": {
"Name": "CVE-2014-9471", "Name": "CVE-2014-9471",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471", "Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471",
"Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.", "Description": "The parse_datetime function in GNU coreutils allows remote attackers to cause a denial of service (crash) or possibly execute arbitrary code via a crafted date string, as demonstrated by the \"--date=TZ=\"123\"345\" @1\" string to the touch or date command.",
"Severity": "Low", "Severity": "Low",
@ -439,7 +439,7 @@ Server: clair
``` ```
#### DELETE /namespaces/`:nsName`/vulnerabilities/`:vulnName` #### DELETE /namespaces/`:nsID`/vulnerabilities/`:vulnName`
###### Description ###### Description
@ -449,7 +449,7 @@ If this vulnerability was inserted by a Fetcher, it may be re-inserted when the
###### Example Request ###### Example Request
```json ```json
GET http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities/CVE-2014-9471 HTTP/1.1 GET http://localhost:6060/v1/namespaces/gAAAAABXTQKgma_TLKq0wr1D6wVB507N3fi9wsWMUypYOSXTDVxQ8OR5L5S6PqZ9Wh0IzWojnVmlspyTL4cyjytyra7U9vAHMA==/vulnerabilities/CVE-2014-9471 HTTP/1.1
``` ```
###### Example Response ###### Example Response
@ -461,7 +461,7 @@ Server: clair
## Fixes ## Fixes
#### GET /namespaces/`:nsName`/vulnerabilities/`:vulnName`/fixes #### GET /namespaces/`:nsID`/vulnerabilities/`:vulnName`/fixes
###### Description ###### Description
@ -470,7 +470,7 @@ The GET route for the Fixes resource displays the list of Features that fix the
###### Example Request ###### Example Request
```json ```json
GET http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities/CVE-2014-9471/fixes HTTP/1.1 GET http://localhost:6060/v1/namespaces/gAAAAABXTQKgma_TLKq0wr1D6wVB507N3fi9wsWMUypYOSXTDVxQ8OR5L5S6PqZ9Wh0IzWojnVmlspyTL4cyjytyra7U9vAHMA==/vulnerabilities/CVE-2014-9471/fixes HTTP/1.1
``` ```
###### Example Response ###### Example Response
@ -484,14 +484,14 @@ Server: clair
"Features": [ "Features": [
{ {
"Name": "coreutils", "Name": "coreutils",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Version": "8.23-1" "Version": "8.23-1"
} }
] ]
} }
``` ```
#### PUT /namespaces/`:nsName`/vulnerabilities/`:vulnName`/fixes/`:featureName` #### PUT /namespaces/`:nsID`/vulnerabilities/`:vulnName`/fixes/`:featureName`
###### Description ###### Description
@ -500,12 +500,12 @@ The PUT route for the Fixes resource updates a Feature that is the fix for a giv
###### Example Request ###### Example Request
```json ```json
PUT http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities/CVE-2014-9471/fixes/coreutils HTTP/1.1 PUT http://localhost:6060/v1/namespaces/gAAAAABXTQKgma_TLKq0wr1D6wVB507N3fi9wsWMUypYOSXTDVxQ8OR5L5S6PqZ9Wh0IzWojnVmlspyTL4cyjytyra7U9vAHMA==/vulnerabilities/CVE-2014-9471/fixes/coreutils HTTP/1.1
{ {
"Feature": { "Feature": {
"Name": "coreutils", "Name": "coreutils",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Version": "4.24-9" "Version": "4.24-9"
} }
} }
@ -520,13 +520,13 @@ Server: clair
{ {
"Feature": { "Feature": {
"Name": "coreutils", "Name": "coreutils",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Version": "4.24-9" "Version": "4.24-9"
} }
} }
``` ```
#### DELETE /namespaces/`:nsName`/vulnerabilities/`:vulnName`/fixes/`:featureName` #### DELETE /namespaces/`:nsID`/vulnerabilities/`:vulnName`/fixes/`:featureName`
###### Description ###### Description
@ -535,7 +535,7 @@ The DELETE route for the Fixes resource removes a Feature as fix for the given V
###### Example Request ###### Example Request
```json ```json
DELETE http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities/CVE-2014-9471/fixes/coreutils DELETE http://localhost:6060/v1/namespaces/gAAAAABXTQKgma_TLKq0wr1D6wVB507N3fi9wsWMUypYOSXTDVxQ8OR5L5S6PqZ9Wh0IzWojnVmlspyTL4cyjytyra7U9vAHMA==/vulnerabilities/CVE-2014-9471/fixes/coreutils
``` ```
###### Example Response ###### Example Response
@ -585,13 +585,13 @@ Server: clair
"New": { "New": {
"Vulnerability": { "Vulnerability": {
"Name": "CVE-TEST", "Name": "CVE-TEST",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Description": "New CVE", "Description": "New CVE",
"Severity": "Low", "Severity": "Low",
"FixedIn": [ "FixedIn": [
{ {
"Name": "grep", "Name": "grep",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Version": "2.25" "Version": "2.25"
} }
] ]
@ -604,7 +604,7 @@ Server: clair
"Old": { "Old": {
"Vulnerability": { "Vulnerability": {
"Name": "CVE-TEST", "Name": "CVE-TEST",
"NamespaceName": "debian:8", "Namespace": {"Name": "debian", "Version": "8"},
"Description": "New CVE", "Description": "New CVE",
"Severity": "Low", "Severity": "Low",
"FixedIn": [] "FixedIn": []

View File

@ -35,7 +35,7 @@ type Error struct {
type Layer struct { type Layer struct {
Name string `json:"Name,omitempty"` Name string `json:"Name,omitempty"`
NamespaceName string `json:"NamespaceName,omitempty"` Namespaces []Namespace `json:"Namespaces,omitempty"`
Path string `json:"Path,omitempty"` Path string `json:"Path,omitempty"`
Headers map[string]string `json:"Headers,omitempty"` Headers map[string]string `json:"Headers,omitempty"`
ParentName string `json:"ParentName,omitempty"` ParentName string `json:"ParentName,omitempty"`
@ -54,27 +54,27 @@ func LayerFromDatabaseModel(dbLayer database.Layer, withFeatures, withVulnerabil
layer.ParentName = dbLayer.Parent.Name layer.ParentName = dbLayer.Parent.Name
} }
if dbLayer.Namespace != nil { for _, namespace := range dbLayer.Namespaces {
layer.NamespaceName = dbLayer.Namespace.Name layer.Namespaces = append(layer.Namespaces, Namespace{Name: namespace.Name, Version: namespace.Version.String()})
} }
if withFeatures || withVulnerabilities && dbLayer.Features != nil { if withFeatures || withVulnerabilities && dbLayer.Features != nil {
for _, dbFeatureVersion := range dbLayer.Features { for _, dbFeatureVersion := range dbLayer.Features {
feature := Feature{ feature := Feature{
Name: dbFeatureVersion.Feature.Name, Name: dbFeatureVersion.Feature.Name,
NamespaceName: dbFeatureVersion.Feature.Namespace.Name, Namespace: Namespace{Name: dbFeatureVersion.Feature.Namespace.Name, Version: dbFeatureVersion.Feature.Namespace.Version.String()},
Version: dbFeatureVersion.Version.String(), Version: dbFeatureVersion.Version.String(),
AddedBy: dbFeatureVersion.AddedBy.Name, AddedBy: dbFeatureVersion.AddedBy.Name,
} }
for _, dbVuln := range dbFeatureVersion.AffectedBy { for _, dbVuln := range dbFeatureVersion.AffectedBy {
vuln := Vulnerability{ vuln := Vulnerability{
Name: dbVuln.Name, Name: dbVuln.Name,
NamespaceName: dbVuln.Namespace.Name, Namespace: Namespace{Name: dbVuln.Namespace.Name, Version: dbVuln.Namespace.Version.String()},
Description: dbVuln.Description, Description: dbVuln.Description,
Link: dbVuln.Link, Link: dbVuln.Link,
Severity: string(dbVuln.Severity), Severity: string(dbVuln.Severity),
Metadata: dbVuln.Metadata, Metadata: dbVuln.Metadata,
} }
if dbVuln.FixedBy != types.MaxVersion { if dbVuln.FixedBy != types.MaxVersion {
@ -90,18 +90,20 @@ func LayerFromDatabaseModel(dbLayer database.Layer, withFeatures, withVulnerabil
} }
type Namespace struct { type Namespace struct {
Name string `json:"Name,omitempty"` ID string `json:"ID,omitempty"`
Name string `json:"Name,omitempty"`
Version string `json:"Version,omitempty"`
} }
type Vulnerability struct { type Vulnerability struct {
Name string `json:"Name,omitempty"` Name string `json:"Name,omitempty"`
NamespaceName string `json:"NamespaceName,omitempty"` Namespace Namespace `json:"Namespace,omitempty"`
Description string `json:"Description,omitempty"` Description string `json:"Description,omitempty"`
Link string `json:"Link,omitempty"` Link string `json:"Link,omitempty"`
Severity string `json:"Severity,omitempty"` Severity string `json:"Severity,omitempty"`
Metadata map[string]interface{} `json:"Metadata,omitempty"` Metadata map[string]interface{} `json:"Metadata,omitempty"`
FixedBy string `json:"FixedBy,omitempty"` FixedBy string `json:"FixedBy,omitempty"`
FixedIn []Feature `json:"FixedIn,omitempty"` FixedIn []Feature `json:"FixedIn,omitempty"`
} }
func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) { func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) {
@ -110,6 +112,11 @@ func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) {
return database.Vulnerability{}, errors.New("Invalid severity") return database.Vulnerability{}, errors.New("Invalid severity")
} }
version, err := types.NewVersion(v.Namespace.Version)
if err != nil {
return database.Vulnerability{}, errors.New("Invalid namespace version")
}
var dbFeatures []database.FeatureVersion var dbFeatures []database.FeatureVersion
for _, feature := range v.FixedIn { for _, feature := range v.FixedIn {
dbFeature, err := feature.DatabaseModel() dbFeature, err := feature.DatabaseModel()
@ -122,7 +129,7 @@ func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) {
return database.Vulnerability{ return database.Vulnerability{
Name: v.Name, Name: v.Name,
Namespace: database.Namespace{Name: v.NamespaceName}, Namespace: database.Namespace{Name: v.Namespace.Name, Version: version},
Description: v.Description, Description: v.Description,
Link: v.Link, Link: v.Link,
Severity: severity, Severity: severity,
@ -133,12 +140,12 @@ func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) {
func VulnerabilityFromDatabaseModel(dbVuln database.Vulnerability, withFixedIn bool) Vulnerability { func VulnerabilityFromDatabaseModel(dbVuln database.Vulnerability, withFixedIn bool) Vulnerability {
vuln := Vulnerability{ vuln := Vulnerability{
Name: dbVuln.Name, Name: dbVuln.Name,
NamespaceName: dbVuln.Namespace.Name, Namespace: Namespace{Name: dbVuln.Namespace.Name, Version: dbVuln.Namespace.Version.String()},
Description: dbVuln.Description, Description: dbVuln.Description,
Link: dbVuln.Link, Link: dbVuln.Link,
Severity: string(dbVuln.Severity), Severity: string(dbVuln.Severity),
Metadata: dbVuln.Metadata, Metadata: dbVuln.Metadata,
} }
if withFixedIn { if withFixedIn {
@ -152,7 +159,7 @@ func VulnerabilityFromDatabaseModel(dbVuln database.Vulnerability, withFixedIn b
type Feature struct { type Feature struct {
Name string `json:"Name,omitempty"` Name string `json:"Name,omitempty"`
NamespaceName string `json:"NamespaceName,omitempty"` Namespace Namespace `json:"Namespace,omitempty"`
Version string `json:"Version,omitempty"` Version string `json:"Version,omitempty"`
Vulnerabilities []Vulnerability `json:"Vulnerabilities,omitempty"` Vulnerabilities []Vulnerability `json:"Vulnerabilities,omitempty"`
AddedBy string `json:"AddedBy,omitempty"` AddedBy string `json:"AddedBy,omitempty"`
@ -165,10 +172,10 @@ func FeatureFromDatabaseModel(dbFeatureVersion database.FeatureVersion) Feature
} }
return Feature{ return Feature{
Name: dbFeatureVersion.Feature.Name, Name: dbFeatureVersion.Feature.Name,
NamespaceName: dbFeatureVersion.Feature.Namespace.Name, Namespace: Namespace{Name: dbFeatureVersion.Feature.Namespace.Name, Version: dbFeatureVersion.Feature.Namespace.Version.String()},
Version: versionStr, Version: versionStr,
AddedBy: dbFeatureVersion.AddedBy.Name, AddedBy: dbFeatureVersion.AddedBy.Name,
} }
} }
@ -184,10 +191,15 @@ func (f Feature) DatabaseModel() (database.FeatureVersion, error) {
} }
} }
nsVersion, err := types.NewVersion(f.Namespace.Version)
if err != nil {
return database.FeatureVersion{}, err
}
return database.FeatureVersion{ return database.FeatureVersion{
Feature: database.Feature{ Feature: database.Feature{
Name: f.Name, Name: f.Name,
Namespace: database.Namespace{Name: f.NamespaceName}, Namespace: database.Namespace{Name: f.Namespace.Name, Version: nsVersion},
}, },
Version: version, Version: version,
}, nil }, nil

View File

@ -34,16 +34,16 @@ func NewRouter(ctx *context.RouteContext) *httprouter.Router {
router.GET("/namespaces", context.HTTPHandler(getNamespaces, ctx)) router.GET("/namespaces", context.HTTPHandler(getNamespaces, ctx))
// Vulnerabilities // Vulnerabilities
router.GET("/namespaces/:namespaceName/vulnerabilities", context.HTTPHandler(getVulnerabilities, ctx)) router.GET("/namespaces/:namespaceID/vulnerabilities", context.HTTPHandler(getVulnerabilities, ctx))
router.POST("/namespaces/:namespaceName/vulnerabilities", context.HTTPHandler(postVulnerability, ctx)) router.POST("/namespaces/:namespaceID/vulnerabilities", context.HTTPHandler(postVulnerability, ctx))
router.GET("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", context.HTTPHandler(getVulnerability, ctx)) router.GET("/namespaces/:namespaceID/vulnerabilities/:vulnerabilityName", context.HTTPHandler(getVulnerability, ctx))
router.PUT("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", context.HTTPHandler(putVulnerability, ctx)) router.PUT("/namespaces/:namespaceID/vulnerabilities/:vulnerabilityName", context.HTTPHandler(putVulnerability, ctx))
router.DELETE("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName", context.HTTPHandler(deleteVulnerability, ctx)) router.DELETE("/namespaces/:namespaceID/vulnerabilities/:vulnerabilityName", context.HTTPHandler(deleteVulnerability, ctx))
// Fixes // Fixes
router.GET("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes", context.HTTPHandler(getFixes, ctx)) router.GET("/namespaces/:namespaceID/vulnerabilities/:vulnerabilityName/fixes", context.HTTPHandler(getFixes, ctx))
router.PUT("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes/:fixName", context.HTTPHandler(putFix, ctx)) router.PUT("/namespaces/:namespaceID/vulnerabilities/:vulnerabilityName/fixes/:fixName", context.HTTPHandler(putFix, ctx))
router.DELETE("/namespaces/:namespaceName/vulnerabilities/:vulnerabilityName/fixes/:fixName", context.HTTPHandler(deleteFix, ctx)) router.DELETE("/namespaces/:namespaceID/vulnerabilities/:vulnerabilityName/fixes/:fixName", context.HTTPHandler(deleteFix, ctx))
// Notifications // Notifications
router.GET("/notifications/:notificationName", context.HTTPHandler(getNotification, ctx)) router.GET("/notifications/:notificationName", context.HTTPHandler(getNotification, ctx))

View File

@ -179,7 +179,12 @@ func getNamespaces(w http.ResponseWriter, r *http.Request, p httprouter.Params,
} }
var namespaces []Namespace var namespaces []Namespace
for _, dbNamespace := range dbNamespaces { for _, dbNamespace := range dbNamespaces {
namespaces = append(namespaces, Namespace{Name: dbNamespace.Name}) if namespaceIDBytes, err := tokenMarshal(dbNamespace.ID, ctx.Config.PaginationKey); err != nil {
writeResponse(w, r, http.StatusInternalServerError, NamespaceEnvelope{Error: &Error{err.Error()}})
return getNamespacesRoute, http.StatusInternalServerError
} else {
namespaces = append(namespaces, Namespace{ID: string(namespaceIDBytes), Name: dbNamespace.Name, Version: dbNamespace.Version.String()})
}
} }
writeResponse(w, r, http.StatusOK, NamespaceEnvelope{Namespaces: &namespaces}) writeResponse(w, r, http.StatusOK, NamespaceEnvelope{Namespaces: &namespaces})
@ -213,13 +218,13 @@ func getVulnerabilities(w http.ResponseWriter, r *http.Request, p httprouter.Par
} }
} }
namespace := p.ByName("namespaceName") namespaceID := 0
if namespace == "" { if err = tokenUnmarshal(p.ByName("namespaceID"), ctx.Config.PaginationKey, &namespaceID); err != nil {
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"namespace should not be empty"}}) writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"invalid namespace id format: " + err.Error()}})
return getNotificationRoute, http.StatusBadRequest return getNotificationRoute, http.StatusBadRequest
} }
dbVulns, nextPage, err := ctx.Store.ListVulnerabilities(namespace, limit, page) dbVulns, nextPage, err := ctx.Store.ListVulnerabilities(namespaceID, limit, page)
if err == cerrors.ErrNotFound { if err == cerrors.ErrNotFound {
writeResponse(w, r, http.StatusNotFound, VulnerabilityEnvelope{Error: &Error{err.Error()}}) writeResponse(w, r, http.StatusNotFound, VulnerabilityEnvelope{Error: &Error{err.Error()}})
return getVulnerabilityRoute, http.StatusNotFound return getVulnerabilityRoute, http.StatusNotFound
@ -286,7 +291,13 @@ func postVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Para
func getVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) { func getVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
_, withFixedIn := r.URL.Query()["fixedIn"] _, withFixedIn := r.URL.Query()["fixedIn"]
dbVuln, err := ctx.Store.FindVulnerability(p.ByName("namespaceName"), p.ByName("vulnerabilityName")) namespaceID := 0
if err := tokenUnmarshal(p.ByName("namespaceID"), ctx.Config.PaginationKey, &namespaceID); err != nil {
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"invalid namespace id format: " + err.Error()}})
return getVulnerabilityRoute, http.StatusBadRequest
}
dbVuln, err := ctx.Store.FindVulnerability(namespaceID, p.ByName("vulnerabilityName"))
if err == cerrors.ErrNotFound { if err == cerrors.ErrNotFound {
writeResponse(w, r, http.StatusNotFound, VulnerabilityEnvelope{Error: &Error{err.Error()}}) writeResponse(w, r, http.StatusNotFound, VulnerabilityEnvelope{Error: &Error{err.Error()}})
return getVulnerabilityRoute, http.StatusNotFound return getVulnerabilityRoute, http.StatusNotFound
@ -302,6 +313,12 @@ func getVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Param
} }
func putVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) { func putVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
namespaceID := 0
if err := tokenUnmarshal(p.ByName("namespaceID"), ctx.Config.PaginationKey, &namespaceID); err != nil {
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"invalid namespace id format: " + err.Error()}})
return putVulnerabilityRoute, http.StatusBadRequest
}
request := VulnerabilityEnvelope{} request := VulnerabilityEnvelope{}
err := decodeJSON(r, &request) err := decodeJSON(r, &request)
if err != nil { if err != nil {
@ -325,7 +342,7 @@ func putVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Param
return putVulnerabilityRoute, http.StatusBadRequest return putVulnerabilityRoute, http.StatusBadRequest
} }
vuln.Namespace.Name = p.ByName("namespaceName") vuln.Namespace.ID = namespaceID
vuln.Name = p.ByName("vulnerabilityName") vuln.Name = p.ByName("vulnerabilityName")
err = ctx.Store.InsertVulnerabilities([]database.Vulnerability{vuln}, true) err = ctx.Store.InsertVulnerabilities([]database.Vulnerability{vuln}, true)
@ -345,7 +362,13 @@ func putVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Param
} }
func deleteVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) { func deleteVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
err := ctx.Store.DeleteVulnerability(p.ByName("namespaceName"), p.ByName("vulnerabilityName")) namespaceID := 0
if err := tokenUnmarshal(p.ByName("namespaceID"), ctx.Config.PaginationKey, &namespaceID); err != nil {
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"invalid namespace id format: " + err.Error()}})
return deleteVulnerabilityRoute, http.StatusBadRequest
}
err := ctx.Store.DeleteVulnerability(namespaceID, p.ByName("vulnerabilityName"))
if err == cerrors.ErrNotFound { if err == cerrors.ErrNotFound {
writeResponse(w, r, http.StatusNotFound, VulnerabilityEnvelope{Error: &Error{err.Error()}}) writeResponse(w, r, http.StatusNotFound, VulnerabilityEnvelope{Error: &Error{err.Error()}})
return deleteVulnerabilityRoute, http.StatusNotFound return deleteVulnerabilityRoute, http.StatusNotFound
@ -359,7 +382,13 @@ func deleteVulnerability(w http.ResponseWriter, r *http.Request, p httprouter.Pa
} }
func getFixes(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) { func getFixes(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
dbVuln, err := ctx.Store.FindVulnerability(p.ByName("namespaceName"), p.ByName("vulnerabilityName")) namespaceID := 0
if err := tokenUnmarshal(p.ByName("namespaceID"), ctx.Config.PaginationKey, &namespaceID); err != nil {
writeResponse(w, r, http.StatusBadRequest, VulnerabilityEnvelope{Error: &Error{"invalid namespace id format: " + err.Error()}})
return getFixesRoute, http.StatusBadRequest
}
dbVuln, err := ctx.Store.FindVulnerability(namespaceID, p.ByName("vulnerabilityName"))
if err == cerrors.ErrNotFound { if err == cerrors.ErrNotFound {
writeResponse(w, r, http.StatusNotFound, FeatureEnvelope{Error: &Error{err.Error()}}) writeResponse(w, r, http.StatusNotFound, FeatureEnvelope{Error: &Error{err.Error()}})
return getFixesRoute, http.StatusNotFound return getFixesRoute, http.StatusNotFound
@ -374,6 +403,12 @@ func getFixes(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *
} }
func putFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) { func putFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
namespaceID := 0
if err := tokenUnmarshal(p.ByName("namespaceID"), ctx.Config.PaginationKey, &namespaceID); err != nil {
writeResponse(w, r, http.StatusBadRequest, FeatureEnvelope{Error: &Error{"invalid namespace id format: " + err.Error()}})
return putFixRoute, http.StatusBadRequest
}
request := FeatureEnvelope{} request := FeatureEnvelope{}
err := decodeJSON(r, &request) err := decodeJSON(r, &request)
if err != nil { if err != nil {
@ -397,7 +432,7 @@ func putFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *co
return putFixRoute, http.StatusBadRequest return putFixRoute, http.StatusBadRequest
} }
err = ctx.Store.InsertVulnerabilityFixes(p.ByName("vulnerabilityNamespace"), p.ByName("vulnerabilityName"), []database.FeatureVersion{dbFix}) err = ctx.Store.InsertVulnerabilityFixes(namespaceID, p.ByName("vulnerabilityName"), []database.FeatureVersion{dbFix})
if err != nil { if err != nil {
switch err.(type) { switch err.(type) {
case *cerrors.ErrBadRequest: case *cerrors.ErrBadRequest:
@ -418,7 +453,13 @@ func putFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *co
} }
func deleteFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) { func deleteFix(w http.ResponseWriter, r *http.Request, p httprouter.Params, ctx *context.RouteContext) (string, int) {
err := ctx.Store.DeleteVulnerabilityFix(p.ByName("vulnerabilityNamespace"), p.ByName("vulnerabilityName"), p.ByName("fixName")) namespaceID := 0
if err := tokenUnmarshal(p.ByName("namespaceID"), ctx.Config.PaginationKey, &namespaceID); err != nil {
writeResponse(w, r, http.StatusBadRequest, FeatureEnvelope{Error: &Error{"invalid namespace id format: " + err.Error()}})
return deleteFixRoute, http.StatusBadRequest
}
err := ctx.Store.DeleteVulnerabilityFix(namespaceID, p.ByName("vulnerabilityName"), p.ByName("fixName"))
if err == cerrors.ErrNotFound { if err == cerrors.ErrNotFound {
writeResponse(w, r, http.StatusNotFound, FeatureEnvelope{Error: &Error{err.Error()}}) writeResponse(w, r, http.StatusNotFound, FeatureEnvelope{Error: &Error{err.Error()}})
return deleteFixRoute, http.StatusNotFound return deleteFixRoute, http.StatusNotFound

View File

@ -67,6 +67,8 @@ type Datastore interface {
// # Namespace // # Namespace
// ListNamespaces returns the entire list of known Namespaces. // ListNamespaces returns the entire list of known Namespaces.
ListNamespaces() ([]Namespace, error) ListNamespaces() ([]Namespace, error)
// GetNamespaceID returns the id by name and version.
GetNamespaceID(Namespace) (int, error)
// # Layer // # Layer
// InsertLayer stores a Layer in the database. // InsertLayer stores a Layer in the database.
@ -93,7 +95,7 @@ type Datastore interface {
// The Limit and page parameters are used to paginate the return list. // The Limit and page parameters are used to paginate the return list.
// The first given page should be 0. The function will then return the next available page. // The first given page should be 0. The function will then return the next available page.
// If there is no more page, -1 has to be returned. // If there is no more page, -1 has to be returned.
ListVulnerabilities(namespace Namespace, limit int, page int) ([]Vulnerability, int, error) ListVulnerabilities(namespaceID int, limit int, page int) ([]Vulnerability, int, error)
// InsertVulnerabilities stores the given Vulnerabilities in the database, updating them if // InsertVulnerabilities stores the given Vulnerabilities in the database, updating them if
// necessary. A vulnerability is uniquely identified by its Namespace and its Name. // necessary. A vulnerability is uniquely identified by its Namespace and its Name.
@ -110,22 +112,22 @@ type Datastore interface {
InsertVulnerabilities(vulnerabilities []Vulnerability, createNotification bool) error InsertVulnerabilities(vulnerabilities []Vulnerability, createNotification bool) error
// FindVulnerability retrieves a Vulnerability from the database, including the FixedIn list. // FindVulnerability retrieves a Vulnerability from the database, including the FixedIn list.
FindVulnerability(namespace Namespace, name string) (Vulnerability, error) FindVulnerability(namespaceID int, name string) (Vulnerability, error)
// DeleteVulnerability removes a Vulnerability from the database. // DeleteVulnerability removes a Vulnerability from the database.
// It has to create a Notification that will contain the old Vulnerability. // It has to create a Notification that will contain the old Vulnerability.
DeleteVulnerability(namespace Namespace, name string) error DeleteVulnerability(namespaceID int, name string) error
// InsertVulnerabilityFixes adds new FixedIn Feature or update the Versions of existing ones to // InsertVulnerabilityFixes adds new FixedIn Feature or update the Versions of existing ones to
// the specified Vulnerability in the database. // the specified Vulnerability in the database.
// It has has to create a Notification that will contain the old and the updated Vulnerability. // It has has to create a Notification that will contain the old and the updated Vulnerability.
InsertVulnerabilityFixes(vulnerabilityNamespace Namespace, vulnerabilityName string, fixes []FeatureVersion) error InsertVulnerabilityFixes(vulnerabilityNamespaceID int, vulnerabilityName string, fixes []FeatureVersion) error
// DeleteVulnerabilityFix removes a FixedIn Feature from the specified Vulnerability in the // DeleteVulnerabilityFix removes a FixedIn Feature from the specified Vulnerability in the
// database. It can be used to store the fact that a Vulnerability no longer affects the given // database. It can be used to store the fact that a Vulnerability no longer affects the given
// Feature in any Version. // Feature in any Version.
// It has has to create a Notification that will contain the old and the updated Vulnerability. // It has has to create a Notification that will contain the old and the updated Vulnerability.
DeleteVulnerabilityFix(vulnerabilityNamespace Namespace, vulnerabilityName, featureName string) error DeleteVulnerabilityFix(vulnerabilityNamespaceID int, vulnerabilityName, featureName string) error
// # Notification // # Notification
// GetAvailableNotification returns the Name, Created, Notified and Deleted fields of a // GetAvailableNotification returns the Name, Created, Notified and Deleted fields of a

View File

@ -20,15 +20,16 @@ import "time"
// The default behavior of each method is to simply panic. // The default behavior of each method is to simply panic.
type MockDatastore struct { type MockDatastore struct {
FctListNamespaces func() ([]Namespace, error) FctListNamespaces func() ([]Namespace, error)
FctGetNamespaceID func(namespace Namespace) (int, error)
FctInsertLayer func(Layer) error FctInsertLayer func(Layer) error
FctFindLayer func(name string, withFeatures, withVulnerabilities bool) (Layer, error) FctFindLayer func(name string, withFeatures, withVulnerabilities bool) (Layer, error)
FctDeleteLayer func(name string) error FctDeleteLayer func(name string) error
FctListVulnerabilities func(namespace Namespace, limit int, page int) ([]Vulnerability, int, error) FctListVulnerabilities func(namespaceID int, limit int, page int) ([]Vulnerability, int, error)
FctInsertVulnerabilities func(vulnerabilities []Vulnerability, createNotification bool) error FctInsertVulnerabilities func(vulnerabilities []Vulnerability, createNotification bool) error
FctFindVulnerability func(namespace Namespace, name string) (Vulnerability, error) FctFindVulnerability func(namespaceID int, name string) (Vulnerability, error)
FctDeleteVulnerability func(namespace Namespace, name string) error FctDeleteVulnerability func(namespaceID int, name string) error
FctInsertVulnerabilityFixes func(vulnerabilityNamespace Namespace, vulnerabilityName string, fixes []FeatureVersion) error FctInsertVulnerabilityFixes func(vulnerabilityNamespaceID int, vulnerabilityName string, fixes []FeatureVersion) error
FctDeleteVulnerabilityFix func(vulnerabilityNamespace Namespace, vulnerabilityName, featureName string) error FctDeleteVulnerabilityFix func(vulnerabilityNamespaceID int, vulnerabilityName, featureName string) error
FctGetAvailableNotification func(renotifyInterval time.Duration) (VulnerabilityNotification, error) FctGetAvailableNotification func(renotifyInterval time.Duration) (VulnerabilityNotification, error)
FctGetNotification func(name string, limit int, page VulnerabilityNotificationPageNumber) (VulnerabilityNotification, VulnerabilityNotificationPageNumber, error) FctGetNotification func(name string, limit int, page VulnerabilityNotificationPageNumber) (VulnerabilityNotification, VulnerabilityNotificationPageNumber, error)
FctSetNotificationNotified func(name string) error FctSetNotificationNotified func(name string) error
@ -49,6 +50,13 @@ func (mds *MockDatastore) ListNamespaces() ([]Namespace, error) {
panic("required mock function not implemented") panic("required mock function not implemented")
} }
func (mds *MockDatastore) GetNamespaceID(namespace Namespace) (int, error) {
if mds.FctGetNamespaceID != nil {
return mds.FctGetNamespaceID(namespace)
}
panic("required mock function not implemented")
}
func (mds *MockDatastore) InsertLayer(layer Layer) error { func (mds *MockDatastore) InsertLayer(layer Layer) error {
if mds.FctInsertLayer != nil { if mds.FctInsertLayer != nil {
return mds.FctInsertLayer(layer) return mds.FctInsertLayer(layer)
@ -70,9 +78,9 @@ func (mds *MockDatastore) DeleteLayer(name string) error {
panic("required mock function not implemented") panic("required mock function not implemented")
} }
func (mds *MockDatastore) ListVulnerabilities(namespace Namespace, limit int, page int) ([]Vulnerability, int, error) { func (mds *MockDatastore) ListVulnerabilities(namespaceID int, limit int, page int) ([]Vulnerability, int, error) {
if mds.FctListVulnerabilities != nil { if mds.FctListVulnerabilities != nil {
return mds.FctListVulnerabilities(namespace, limit, page) return mds.FctListVulnerabilities(namespaceID, limit, page)
} }
panic("required mock function not implemented") panic("required mock function not implemented")
} }
@ -84,30 +92,30 @@ func (mds *MockDatastore) InsertVulnerabilities(vulnerabilities []Vulnerability,
panic("required mock function not implemented") panic("required mock function not implemented")
} }
func (mds *MockDatastore) FindVulnerability(namespace Namespace, name string) (Vulnerability, error) { func (mds *MockDatastore) FindVulnerability(namespaceID int, name string) (Vulnerability, error) {
if mds.FctFindVulnerability != nil { if mds.FctFindVulnerability != nil {
return mds.FctFindVulnerability(namespace, name) return mds.FctFindVulnerability(namespaceID, name)
} }
panic("required mock function not implemented") panic("required mock function not implemented")
} }
func (mds *MockDatastore) DeleteVulnerability(namespace Namespace, name string) error { func (mds *MockDatastore) DeleteVulnerability(namespaceID int, name string) error {
if mds.FctDeleteVulnerability != nil { if mds.FctDeleteVulnerability != nil {
return mds.FctDeleteVulnerability(namespace, name) return mds.FctDeleteVulnerability(namespaceID, name)
} }
panic("required mock function not implemented") panic("required mock function not implemented")
} }
func (mds *MockDatastore) InsertVulnerabilityFixes(vulnerabilityNamespace Namespace, vulnerabilityName string, fixes []FeatureVersion) error { func (mds *MockDatastore) InsertVulnerabilityFixes(vulnerabilityNamespaceID int, vulnerabilityName string, fixes []FeatureVersion) error {
if mds.FctInsertVulnerabilityFixes != nil { if mds.FctInsertVulnerabilityFixes != nil {
return mds.FctInsertVulnerabilityFixes(vulnerabilityNamespace, vulnerabilityName, fixes) return mds.FctInsertVulnerabilityFixes(vulnerabilityNamespaceID, vulnerabilityName, fixes)
} }
panic("required mock function not implemented") panic("required mock function not implemented")
} }
func (mds *MockDatastore) DeleteVulnerabilityFix(vulnerabilityNamespace Namespace, vulnerabilityName, featureName string) error { func (mds *MockDatastore) DeleteVulnerabilityFix(vulnerabilityNamespaceID int, vulnerabilityName, featureName string) error {
if mds.FctDeleteVulnerabilityFix != nil { if mds.FctDeleteVulnerabilityFix != nil {
return mds.FctDeleteVulnerabilityFix(vulnerabilityNamespace, vulnerabilityName, featureName) return mds.FctDeleteVulnerabilityFix(vulnerabilityNamespaceID, vulnerabilityName, featureName)
} }
panic("required mock function not implemented") panic("required mock function not implemented")
} }

View File

@ -21,13 +21,13 @@ var DebianReleasesMapping = map[string]string{
"wheezy": "7", "wheezy": "7",
"jessie": "8", "jessie": "8",
"stretch": "9", "stretch": "9",
"sid": "unstable", "sid": "10",
// Class names // Class names
"oldstable": "7", "oldstable": "7",
"stable": "8", "stable": "8",
"testing": "9", "testing": "9",
"unstable": "unstable", "unstable": "10",
} }
// UbuntuReleasesMapping translates Ubuntu code names to version numbers // UbuntuReleasesMapping translates Ubuntu code names to version numbers

View File

@ -52,28 +52,6 @@ func (pgSQL *pgSQL) FindLayer(name string, withFeatures, withVulnerabilities boo
Model: database.Model{ID: int(parentID.Int64)}, Model: database.Model{ID: int(parentID.Int64)},
Name: parentName.String, Name: parentName.String,
} }
// Find its parent's namespaces
t = time.Now()
rows, err := pgSQL.Query(searchLayerNamespace, parentID)
observeQueryTime("FindLayer", "searchParentLayerNamespace", t)
if err != nil {
return layer, handleError("searchLayerNamespace", err)
}
defer rows.Close()
for rows.Next() {
var pn database.Namespace
err = rows.Scan(&pn.ID, &pn.Name, &pn.Version)
if err != nil {
return layer, handleError("searchLayerNamespace.Scan()", err)
}
layer.Parent.Namespaces = append(layer.Parent.Namespaces, pn)
}
if err = rows.Err(); err != nil {
return layer, handleError("searchLayerNamespace.Rows()", err)
}
} }
// Find its namespaces // Find its namespaces

View File

@ -208,23 +208,29 @@ func testInsertLayerTree(t *testing.T, datastore database.Datastore) {
}, },
// This layer changes the namespace and adds Features. // This layer changes the namespace and adds Features.
{ {
Name: "TestInsertLayer3", Name: "TestInsertLayer3",
Parent: &database.Layer{Name: "TestInsertLayer2"}, Parent: &database.Layer{Name: "TestInsertLayer2",
Namespaces: []database.Namespace{testInsertLayerNamespace1},
},
Namespaces: []database.Namespace{testInsertLayerNamespace2}, Namespaces: []database.Namespace{testInsertLayerNamespace2},
Features: []database.FeatureVersion{f1, f2, f3}, Features: []database.FeatureVersion{f1, f2, f3},
}, },
// This layer covers the case where the last layer doesn't provide any new Feature. // This layer covers the case where the last layer doesn't provide any new Feature.
{ {
Name: "TestInsertLayer4a", Name: "TestInsertLayer4a",
Parent: &database.Layer{Name: "TestInsertLayer3"}, Parent: &database.Layer{Name: "TestInsertLayer3",
Namespaces: []database.Namespace{testInsertLayerNamespace1, testInsertLayerNamespace2},
},
Features: []database.FeatureVersion{f1, f2, f3}, Features: []database.FeatureVersion{f1, f2, f3},
}, },
// This layer covers the case where the last layer provides Features. // This layer covers the case where the last layer provides Features.
// It also modifies the Namespace ("upgrade") but keeps some Features not upgraded, their // It also modifies the Namespace ("upgrade") but keeps some Features not upgraded, their
// Namespaces should then remain unchanged. // Namespaces should then remain unchanged.
{ {
Name: "TestInsertLayer4b", Name: "TestInsertLayer4b",
Parent: &database.Layer{Name: "TestInsertLayer3"}, Parent: &database.Layer{Name: "TestInsertLayer3",
Namespaces: []database.Namespace{testInsertLayerNamespace1, testInsertLayerNamespace2},
},
Namespaces: []database.Namespace{testInsertLayerNamespace3}, Namespaces: []database.Namespace{testInsertLayerNamespace3},
Features: []database.FeatureVersion{ Features: []database.FeatureVersion{
// Deletes TestInsertLayerFeature1. // Deletes TestInsertLayerFeature1.
@ -295,6 +301,8 @@ func testInsertLayerUpdate(t *testing.T, datastore database.Datastore) {
} }
l3, _ := datastore.FindLayer("TestInsertLayer3", true, false) l3, _ := datastore.FindLayer("TestInsertLayer3", true, false)
l3Parent, _ := datastore.FindLayer(l3.Parent.Name, false, false)
l3.Parent = &l3Parent
l3u := database.Layer{ l3u := database.Layer{
Name: l3.Name, Name: l3.Name,
Parent: l3.Parent, Parent: l3.Parent,
@ -304,7 +312,7 @@ func testInsertLayerUpdate(t *testing.T, datastore database.Datastore) {
l4u := database.Layer{ l4u := database.Layer{
Name: "TestInsertLayer4", Name: "TestInsertLayer4",
Parent: &database.Layer{Name: "TestInsertLayer3"}, Parent: &database.Layer{Name: "TestInsertLayer3", Namespaces: l3.Namespaces},
Features: []database.FeatureVersion{f7}, Features: []database.FeatureVersion{f7},
EngineVersion: 2, EngineVersion: 2,
} }

View File

@ -1,4 +1,4 @@
-- Copyright 2015 clair authors -- Copyright 2016 clair authors
-- --
-- Licensed under the Apache License, Version 2.0 (the "License"); -- Licensed under the Apache License, Version 2.0 (the "License");
-- you may not use this file except in compliance with the License. -- you may not use this file except in compliance with the License.
@ -33,7 +33,7 @@ CREATE INDEX ON LayerNamespace (layer_id, namespace_id);
INSERT INTO LayerNamespace(layer_id, namespace_id) INSERT INTO LayerNamespace(layer_id, namespace_id)
SELECT id, namespace_id SELECT id, namespace_id
from Layer; from Layer WHERE namespace_id IS NOT NULL;
-- ----------------------------------------------------- -- -----------------------------------------------------
-- Layer table -- Layer table

View File

@ -50,6 +50,30 @@ func (pgSQL *pgSQL) insertNamespace(namespace database.Namespace) (int, error) {
return id, nil return id, nil
} }
func (pgSQL *pgSQL) GetNamespaceID(namespace database.Namespace) (int, error) {
if pgSQL.cache != nil {
promCacheQueriesTotal.WithLabelValues("namespace").Inc()
if id, found := pgSQL.cache.Get("namespace:" + namespace.Name + ":" + namespace.Version.String()); found {
promCacheHitsTotal.WithLabelValues("namespace").Inc()
return id.(int), nil
}
}
defer observeQueryTime("getNamespaceID", "all", time.Now())
var id int
err := pgSQL.QueryRow(getNamespaceID, namespace.Name, namespace.Version.String()).Scan(&id)
if err != nil {
return 0, handleError("getNamespaceID", err)
}
if pgSQL.cache != nil {
pgSQL.cache.Add("namespace:"+namespace.Name+":"+namespace.Version.String(), id)
}
return id, nil
}
func (pgSQL *pgSQL) ListNamespaces() (namespaces []database.Namespace, err error) { func (pgSQL *pgSQL) ListNamespaces() (namespaces []database.Namespace, err error) {
rows, err := pgSQL.Query(listNamespace) rows, err := pgSQL.Query(listNamespace)
if err != nil { if err != nil {

View File

@ -118,7 +118,8 @@ func TestNotification(t *testing.T) {
assert.Nil(t, datastore.insertVulnerability(v1, false, true)) assert.Nil(t, datastore.insertVulnerability(v1, false, true))
// Get the notification associated to the previously inserted vulnerability. // Get the notification associated to the previously inserted vulnerability.
notification, err := datastore.GetAvailableNotification(time.Second) var notification database.VulnerabilityNotification
notification, err = datastore.GetAvailableNotification(time.Second)
if assert.Nil(t, err) && assert.NotEmpty(t, notification.Name) { if assert.Nil(t, err) && assert.NotEmpty(t, notification.Name) {
// Verify the renotify behaviour. // Verify the renotify behaviour.
@ -206,8 +207,12 @@ func TestNotification(t *testing.T) {
} }
} }
namespaceID, err := datastore.GetNamespaceID(database.Namespace{
Name: "TestNotificationNamespace",
Version: types.NewVersionUnsafe("1.0"),
})
// Delete a vulnerability and verify the notification. // Delete a vulnerability and verify the notification.
if assert.Nil(t, datastore.DeleteVulnerability(v1b.Namespace, v1b.Name)) { if assert.Nil(t, err) && assert.Nil(t, datastore.DeleteVulnerability(namespaceID, v1b.Name)) {
notification, err = datastore.GetAvailableNotification(time.Second) notification, err = datastore.GetAvailableNotification(time.Second)
assert.Nil(t, err) assert.Nil(t, err)
assert.NotEmpty(t, notification.Name) assert.NotEmpty(t, notification.Name)

View File

@ -37,9 +37,9 @@ const (
SELECT id FROM Namespace WHERE name = $1 AND version = $2 SELECT id FROM Namespace WHERE name = $1 AND version = $2
UNION UNION
SELECT id FROM new_namespace` SELECT id FROM new_namespace`
getNamespaceID = `SELECT id FROM Namespace WHERE name = $1 AND version = $2`
searchNamespace = `SELECT id FROM Namespace WHERE name = $1 AND version = $2` listNamespace = `SELECT id, name, version FROM Namespace where version IS NOT NULL`
listNamespace = `SELECT id, name, version FROM Namespace` getNamespaceByID = `SELECT name, version FROM Namespace WHERE id = $1`
// feature.go // feature.go
soiFeature = ` soiFeature = `
@ -161,10 +161,10 @@ const (
searchVulnerabilityBase = ` searchVulnerabilityBase = `
SELECT v.id, v.name, n.id, n.name, n.version, v.description, v.link, v.severity, v.metadata SELECT v.id, v.name, n.id, n.name, n.version, v.description, v.link, v.severity, v.metadata
FROM Vulnerability v JOIN Namespace n ON v.namespace_id = n.id` FROM Vulnerability v JOIN Namespace n ON v.namespace_id = n.id`
searchVulnerabilityForUpdate = ` FOR UPDATE OF v` searchVulnerabilityForUpdate = ` FOR UPDATE OF v`
searchVulnerabilityByNamespaceAndName = ` WHERE n.name = $1 AND n.version = $2 AND v.name = $3 AND v.deleted_at IS NULL` searchVulnerabilityByNamespaceIDAndName = ` WHERE n.id = $1 AND v.name = $2 AND v.deleted_at IS NULL`
searchVulnerabilityByID = ` WHERE v.id = $1` searchVulnerabilityByID = ` WHERE v.id = $1`
searchVulnerabilityByNamespaceID = ` WHERE n.id = $1 AND v.deleted_at IS NULL searchVulnerabilityByNamespaceID = ` WHERE n.id = $1 AND v.deleted_at IS NULL
AND v.id >= $2 AND v.id >= $2
ORDER BY v.id ORDER BY v.id
LIMIT $3` LIMIT $3`
@ -189,8 +189,8 @@ const (
removeVulnerability = ` removeVulnerability = `
UPDATE Vulnerability UPDATE Vulnerability
SET deleted_at = CURRENT_TIMESTAMP SET deleted_at = CURRENT_TIMESTAMP
WHERE namespace_id = (SELECT id FROM Namespace WHERE name = $1 AND version = $2) WHERE namespace_id = $1
AND name = $3 AND name = $2
AND deleted_at IS NULL AND deleted_at IS NULL
RETURNING id` RETURNING id`

View File

@ -28,21 +28,12 @@ import (
"github.com/guregu/null/zero" "github.com/guregu/null/zero"
) )
func (pgSQL *pgSQL) ListVulnerabilities(namespace database.Namespace, limit int, startID int) ([]database.Vulnerability, int, error) { func (pgSQL *pgSQL) ListVulnerabilities(namespaceID int, limit int, startID int) ([]database.Vulnerability, int, error) {
defer observeQueryTime("listVulnerabilities", "all", time.Now()) defer observeQueryTime("listVulnerabilities", "all", time.Now())
// Query Namespace.
var id int
err := pgSQL.QueryRow(searchNamespace, namespace.Name, &namespace.Version).Scan(&id)
if err != nil {
return nil, -1, handleError("searchNamespace", err)
} else if id == 0 {
return nil, -1, cerrors.ErrNotFound
}
// Query. // Query.
query := searchVulnerabilityBase + searchVulnerabilityByNamespaceID query := searchVulnerabilityBase + searchVulnerabilityByNamespaceID
rows, err := pgSQL.Query(query, id, startID, limit+1) rows, err := pgSQL.Query(query, namespaceID, startID, limit+1)
if err != nil { if err != nil {
return nil, -1, handleError("searchVulnerabilityByNamespaceID", err) return nil, -1, handleError("searchVulnerabilityByNamespaceID", err)
} }
@ -84,21 +75,21 @@ func (pgSQL *pgSQL) ListVulnerabilities(namespace database.Namespace, limit int,
return vulns, nextID, nil return vulns, nextID, nil
} }
func (pgSQL *pgSQL) FindVulnerability(namespace database.Namespace, name string) (database.Vulnerability, error) { func (pgSQL *pgSQL) FindVulnerability(namespaceID int, name string) (database.Vulnerability, error) {
return findVulnerability(pgSQL, namespace, name, false) return findVulnerability(pgSQL, namespaceID, name, false)
} }
func findVulnerability(queryer Queryer, namespace database.Namespace, name string, forUpdate bool) (database.Vulnerability, error) { func findVulnerability(queryer Queryer, namespaceID int, name string, forUpdate bool) (database.Vulnerability, error) {
defer observeQueryTime("findVulnerability", "all", time.Now()) defer observeQueryTime("findVulnerability", "all", time.Now())
queryName := "searchVulnerabilityBase+searchVulnerabilityByNamespaceAndName" queryName := "searchVulnerabilityBase+searchVulnerabilityByNamespaceIDAndName"
query := searchVulnerabilityBase + searchVulnerabilityByNamespaceAndName query := searchVulnerabilityBase + searchVulnerabilityByNamespaceIDAndName
if forUpdate { if forUpdate {
queryName = queryName + "+searchVulnerabilityForUpdate" queryName = queryName + "+searchVulnerabilityForUpdate"
query = query + searchVulnerabilityForUpdate query = query + searchVulnerabilityForUpdate
} }
return scanVulnerability(queryer, queryName, queryer.QueryRow(query, namespace.Name, &namespace.Version, name)) return scanVulnerability(queryer, queryName, queryer.QueryRow(query, namespaceID, name))
} }
func (pgSQL *pgSQL) findVulnerabilityByIDWithDeleted(id int) (database.Vulnerability, error) { func (pgSQL *pgSQL) findVulnerabilityByIDWithDeleted(id int) (database.Vulnerability, error) {
@ -195,9 +186,24 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
tf := time.Now() tf := time.Now()
// Verify parameters // Verify parameters
if vulnerability.Name == "" || vulnerability.Namespace.IsEmpty() { if vulnerability.Name == "" {
return cerrors.NewBadRequestError("insertVulnerability needs at least the Name and the Namespace") return cerrors.NewBadRequestError("insertVulnerability needs at least the Name")
} }
if vulnerability.Namespace.ID <= 0 {
// Find or insert Vulnerability's Namespace.
namespaceID, err := pgSQL.insertNamespace(vulnerability.Namespace)
if err != nil {
return err
}
vulnerability.Namespace.ID = namespaceID
} else if vulnerability.Namespace.IsEmpty() {
err := pgSQL.QueryRow(getNamespaceByID, vulnerability.Namespace.ID).Scan(&vulnerability.Namespace.Name, &vulnerability.Namespace.Version)
if err != nil {
return cerrors.NewBadRequestError("could not get the related namespace")
}
}
if !onlyFixedIn && !vulnerability.Severity.IsValid() { if !onlyFixedIn && !vulnerability.Severity.IsValid() {
msg := fmt.Sprintf("could not insert a vulnerability that has an invalid Severity: %s", vulnerability.Severity) msg := fmt.Sprintf("could not insert a vulnerability that has an invalid Severity: %s", vulnerability.Severity)
log.Warning(msg) log.Warning(msg)
@ -209,8 +215,7 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
if fifv.Feature.Namespace.IsEmpty() { if fifv.Feature.Namespace.IsEmpty() {
// As there is no Namespace on that FixedIn FeatureVersion, set it to the Vulnerability's // As there is no Namespace on that FixedIn FeatureVersion, set it to the Vulnerability's
// Namespace. // Namespace.
fifv.Feature.Namespace.Name = vulnerability.Namespace.Name fifv.Feature.Namespace = vulnerability.Namespace
fifv.Feature.Namespace.Version = vulnerability.Namespace.Version
} else if !fifv.Feature.Namespace.Equal(vulnerability.Namespace) { } else if !fifv.Feature.Namespace.Equal(vulnerability.Namespace) {
msg := "could not insert an invalid vulnerability that contains FixedIn FeatureVersion that are not in the same namespace as the Vulnerability" msg := "could not insert an invalid vulnerability that contains FixedIn FeatureVersion that are not in the same namespace as the Vulnerability"
log.Warning(msg) log.Warning(msg)
@ -229,7 +234,7 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
} }
// Find existing vulnerability and its Vulnerability_FixedIn_Features (for update). // Find existing vulnerability and its Vulnerability_FixedIn_Features (for update).
existingVulnerability, err := findVulnerability(tx, vulnerability.Namespace, vulnerability.Name, true) existingVulnerability, err := findVulnerability(tx, vulnerability.Namespace.ID, vulnerability.Name, true)
if err != nil && err != cerrors.ErrNotFound { if err != nil && err != cerrors.ErrNotFound {
tx.Rollback() tx.Rollback()
return err return err
@ -267,7 +272,7 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
} }
// Mark the old vulnerability as non latest. // Mark the old vulnerability as non latest.
_, err = tx.Exec(removeVulnerability, vulnerability.Namespace.Name, &vulnerability.Namespace.Version, vulnerability.Name) _, err = tx.Exec(removeVulnerability, vulnerability.Namespace.ID, vulnerability.Name)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return handleError("removeVulnerability", err) return handleError("removeVulnerability", err)
@ -284,16 +289,10 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
vulnerability.FixedIn = fixedIn vulnerability.FixedIn = fixedIn
} }
// Find or insert Vulnerability's Namespace.
namespaceID, err := pgSQL.insertNamespace(vulnerability.Namespace)
if err != nil {
return err
}
// Insert vulnerability. // Insert vulnerability.
err = tx.QueryRow( err = tx.QueryRow(
insertVulnerability, insertVulnerability,
namespaceID, vulnerability.Namespace.ID,
vulnerability.Name, vulnerability.Name,
vulnerability.Description, vulnerability.Description,
vulnerability.Link, vulnerability.Link,
@ -500,38 +499,41 @@ func linkVulnerabilityToFeatureVersions(tx *sql.Tx, fixedInID, vulnerabilityID,
return nil return nil
} }
func (pgSQL *pgSQL) InsertVulnerabilityFixes(vulnerabilityNamespace database.Namespace, vulnerabilityName string, fixes []database.FeatureVersion) error { func (pgSQL *pgSQL) InsertVulnerabilityFixes(vulnerabilityNamespaceID int, vulnerabilityName string, fixes []database.FeatureVersion) error {
defer observeQueryTime("InsertVulnerabilityFixes", "all", time.Now()) defer observeQueryTime("InsertVulnerabilityFixes", "all", time.Now())
var vns database.Namespace
if err := pgSQL.QueryRow(getNamespaceByID, vulnerabilityNamespaceID).Scan(&vns.Name, &vns.Version); err != nil {
return handleError("getNamespaceByID", err)
}
vns.ID = vulnerabilityNamespaceID
v := database.Vulnerability{ v := database.Vulnerability{
Name: vulnerabilityName, Name: vulnerabilityName,
Namespace: database.Namespace{ Namespace: vns,
Name: vulnerabilityNamespace.Name, FixedIn: fixes,
Version: vulnerabilityNamespace.Version,
},
FixedIn: fixes,
} }
return pgSQL.insertVulnerability(v, true, true) return pgSQL.insertVulnerability(v, true, true)
} }
func (pgSQL *pgSQL) DeleteVulnerabilityFix(vulnerabilityNamespace database.Namespace, vulnerabilityName, featureName string) error { func (pgSQL *pgSQL) DeleteVulnerabilityFix(vulnerabilityNamespaceID int, vulnerabilityName, featureName string) error {
defer observeQueryTime("DeleteVulnerabilityFix", "all", time.Now()) defer observeQueryTime("DeleteVulnerabilityFix", "all", time.Now())
var vns database.Namespace
if err := pgSQL.QueryRow(getNamespaceByID, vulnerabilityNamespaceID).Scan(&vns.Name, &vns.Version); err != nil {
return handleError("getNamespaceByID", err)
}
vns.ID = vulnerabilityNamespaceID
v := database.Vulnerability{ v := database.Vulnerability{
Name: vulnerabilityName, Name: vulnerabilityName,
Namespace: database.Namespace{ Namespace: vns,
Name: vulnerabilityNamespace.Name,
Version: vulnerabilityNamespace.Version,
},
FixedIn: []database.FeatureVersion{ FixedIn: []database.FeatureVersion{
{ {
Feature: database.Feature{ Feature: database.Feature{
Name: featureName, Name: featureName,
Namespace: database.Namespace{ Namespace: vns,
Name: vulnerabilityNamespace.Name,
Version: vulnerabilityNamespace.Version,
},
}, },
Version: types.MinVersion, Version: types.MinVersion,
}, },
@ -541,7 +543,7 @@ func (pgSQL *pgSQL) DeleteVulnerabilityFix(vulnerabilityNamespace database.Names
return pgSQL.insertVulnerability(v, true, true) return pgSQL.insertVulnerability(v, true, true)
} }
func (pgSQL *pgSQL) DeleteVulnerability(namespace database.Namespace, name string) error { func (pgSQL *pgSQL) DeleteVulnerability(namespaceID int, name string) error {
defer observeQueryTime("DeleteVulnerability", "all", time.Now()) defer observeQueryTime("DeleteVulnerability", "all", time.Now())
// Begin transaction. // Begin transaction.
@ -552,7 +554,7 @@ func (pgSQL *pgSQL) DeleteVulnerability(namespace database.Namespace, name strin
} }
var vulnerabilityID int var vulnerabilityID int
err = tx.QueryRow(removeVulnerability, namespace.Name, &namespace.Version, name).Scan(&vulnerabilityID) err = tx.QueryRow(removeVulnerability, namespaceID, name).Scan(&vulnerabilityID)
if err != nil { if err != nil {
tx.Rollback() tx.Rollback()
return handleError("removeVulnerability", err) return handleError("removeVulnerability", err)

View File

@ -37,8 +37,12 @@ func TestFindVulnerability(t *testing.T) {
Name: "debian", Name: "debian",
Version: types.NewVersionUnsafe("7"), Version: types.NewVersionUnsafe("7"),
} }
var testExistNamespaceID int
testExistNamespaceID, err = datastore.GetNamespaceID(testExistNamespace)
assert.Nil(t, err)
// Find a vulnerability that does not exist. // Find a vulnerability that does not exist.
_, err = datastore.FindVulnerability(database.Namespace{}, "") _, err = datastore.FindVulnerability(-1, "")
assert.Equal(t, cerrors.ErrNotFound, err) assert.Equal(t, cerrors.ErrNotFound, err)
// Find a normal vulnerability. // Find a normal vulnerability.
@ -60,7 +64,7 @@ func TestFindVulnerability(t *testing.T) {
}, },
} }
v1f, err := datastore.FindVulnerability(testExistNamespace, "CVE-OPENSSL-1-DEB7") v1f, err := datastore.FindVulnerability(testExistNamespaceID, "CVE-OPENSSL-1-DEB7")
if assert.Nil(t, err) { if assert.Nil(t, err) {
equalsVuln(t, &v1, &v1f) equalsVuln(t, &v1, &v1f)
} }
@ -73,7 +77,7 @@ func TestFindVulnerability(t *testing.T) {
Severity: types.Unknown, Severity: types.Unknown,
} }
v2f, err := datastore.FindVulnerability(testExistNamespace, "CVE-NOPE") v2f, err := datastore.FindVulnerability(testExistNamespaceID, "CVE-NOPE")
if assert.Nil(t, err) { if assert.Nil(t, err) {
equalsVuln(t, &v2, &v2f) equalsVuln(t, &v2, &v2f)
} }
@ -91,20 +95,28 @@ func TestDeleteVulnerability(t *testing.T) {
Name: "debian", Name: "debian",
Version: types.NewVersionUnsafe("7"), Version: types.NewVersionUnsafe("7"),
} }
var testExistNamespaceID int
testExistNamespaceID, err = datastore.GetNamespaceID(testExistNamespace)
assert.Nil(t, err)
testNonExistNamespace := database.Namespace{ testNonExistNamespace := database.Namespace{
Name: "TestDeleteVulnerabilityNamespace", Name: "TestDeleteVulnerabilityNamespace",
Version: types.NewVersionUnsafe("1.0"), Version: types.NewVersionUnsafe("1.0"),
} }
// Delete non-existing Vulnerability. var testNonExistNamespaceID int
err = datastore.DeleteVulnerability(testNonExistNamespace, "CVE-OPENSSL-1-DEB7") testNonExistNamespaceID, err = datastore.GetNamespaceID(testNonExistNamespace)
assert.Equal(t, cerrors.ErrNotFound, err) assert.Equal(t, cerrors.ErrNotFound, err)
err = datastore.DeleteVulnerability(testExistNamespace, "TestDeleteVulnerabilityVulnerability1")
// Delete non-existing Vulnerability.
err = datastore.DeleteVulnerability(testNonExistNamespaceID, "CVE-OPENSSL-1-DEB7")
assert.Equal(t, cerrors.ErrNotFound, err)
err = datastore.DeleteVulnerability(testExistNamespaceID, "TestDeleteVulnerabilityVulnerability1")
assert.Equal(t, cerrors.ErrNotFound, err) assert.Equal(t, cerrors.ErrNotFound, err)
// Delete Vulnerability. // Delete Vulnerability.
err = datastore.DeleteVulnerability(testExistNamespace, "CVE-OPENSSL-1-DEB7") err = datastore.DeleteVulnerability(testExistNamespaceID, "CVE-OPENSSL-1-DEB7")
if assert.Nil(t, err) { if assert.Nil(t, err) {
_, err := datastore.FindVulnerability(testExistNamespace, "CVE-OPENSSL-1-DEB7") _, err := datastore.FindVulnerability(testExistNamespaceID, "CVE-OPENSSL-1-DEB7")
assert.Equal(t, cerrors.ErrNotFound, err) assert.Equal(t, cerrors.ErrNotFound, err)
} }
} }
@ -228,7 +240,11 @@ func TestInsertVulnerability(t *testing.T) {
} }
err = datastore.InsertVulnerabilities([]database.Vulnerability{v1}, true) err = datastore.InsertVulnerabilities([]database.Vulnerability{v1}, true)
if assert.Nil(t, err) { if assert.Nil(t, err) {
v1f, err := datastore.FindVulnerability(n1, v1.Name) var n1ID int
var v1f database.Vulnerability
n1ID, err := datastore.GetNamespaceID(n1)
assert.Nil(t, err)
v1f, err = datastore.FindVulnerability(n1ID, v1.Name)
if assert.Nil(t, err) { if assert.Nil(t, err) {
equalsVuln(t, &v1, &v1f) equalsVuln(t, &v1, &v1f)
} }
@ -244,7 +260,11 @@ func TestInsertVulnerability(t *testing.T) {
err = datastore.InsertVulnerabilities([]database.Vulnerability{v1}, true) err = datastore.InsertVulnerabilities([]database.Vulnerability{v1}, true)
if assert.Nil(t, err) { if assert.Nil(t, err) {
v1f, err := datastore.FindVulnerability(n1, v1.Name) var n1ID int
var v1f database.Vulnerability
n1ID, err := datastore.GetNamespaceID(n1)
assert.Nil(t, err)
v1f, err = datastore.FindVulnerability(n1ID, v1.Name)
if assert.Nil(t, err) { if assert.Nil(t, err) {
// We already had f1 before the update. // We already had f1 before the update.
// Add it to the struct for comparison. // Add it to the struct for comparison.

View File

@ -169,6 +169,7 @@ func parseDebianJSON(data *jsonData) (vulnerabilities []database.Vulnerability,
// Determine the version of the package the vulnerability affects. // Determine the version of the package the vulnerability affects.
var version types.Version var version types.Version
var nsVersion types.Version
var err error var err error
if releaseNode.FixedVersion == "0" { if releaseNode.FixedVersion == "0" {
// This means that the package is not affected by this vulnerability. // This means that the package is not affected by this vulnerability.
@ -187,13 +188,19 @@ func parseDebianJSON(data *jsonData) (vulnerabilities []database.Vulnerability,
} }
} }
nsVersion, err = types.NewVersion(database.DebianReleasesMapping[releaseName])
if err != nil {
log.Warningf("could not parse namespace version '%s': %s. skipping", database.DebianReleasesMapping[releaseName], err.Error())
continue
}
// Create and add the feature version. // Create and add the feature version.
pkg := database.FeatureVersion{ pkg := database.FeatureVersion{
Feature: database.Feature{ Feature: database.Feature{
Name: pkgName, Name: pkgName,
Namespace: database.Namespace{ Namespace: database.Namespace{
Name: "debian", Name: "debian",
Version: types.NewVersionUnsafe(database.DebianReleasesMapping[releaseName]), Version: nsVersion,
}, },
}, },
Version: version, Version: version,

View File

@ -48,7 +48,7 @@ func TestDebianParser(t *testing.T) {
}, },
{ {
Feature: database.Feature{ Feature: database.Feature{
Namespace: database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("unstable")}, Namespace: database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("10")},
Name: "aptdaemon", Name: "aptdaemon",
}, },
@ -74,7 +74,7 @@ func TestDebianParser(t *testing.T) {
}, },
{ {
Feature: database.Feature{ Feature: database.Feature{
Namespace: database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("unstable")}, Namespace: database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("10")},
Name: "aptdaemon", Name: "aptdaemon",
}, },
Version: types.NewVersionUnsafe("0.7.0"), Version: types.NewVersionUnsafe("0.7.0"),

View File

@ -291,8 +291,13 @@ func toFeatureVersions(criteria criteria) []database.FeatureVersion {
} }
if osVersion > firstConsideredRHEL { if osVersion > firstConsideredRHEL {
nsVersion, err := types.NewVersion(strconv.Itoa(osVersion))
if err != nil {
log.Warningf("could not parse namespace version '%s': %s. skipping", strconv.Itoa(osVersion), err.Error())
continue
}
featureVersion.Feature.Namespace.Name = "centos" featureVersion.Feature.Namespace.Name = "centos"
featureVersion.Feature.Namespace.Version = types.NewVersionUnsafe(strconv.Itoa(osVersion)) featureVersion.Feature.Namespace.Version = nsVersion
} else { } else {
continue continue
} }

View File

@ -373,10 +373,15 @@ func parseUbuntuCVE(fileContent io.Reader) (vulnerability database.Vulnerability
continue continue
} }
nsVersion, err := types.NewVersion(database.UbuntuReleasesMapping[md["release"]])
if err != nil {
log.Warningf("could not parse namespace version '%s': %s. skipping", database.UbuntuReleasesMapping[md["release"]], err)
}
// Create and add the new package. // Create and add the new package.
featureVersion := database.FeatureVersion{ featureVersion := database.FeatureVersion{
Feature: database.Feature{ Feature: database.Feature{
Namespace: database.Namespace{Name: "ubuntu", Version: types.NewVersionUnsafe(database.UbuntuReleasesMapping[md["release"]])}, Namespace: database.Namespace{Name: "ubuntu", Version: nsVersion},
Name: md["package"], Name: md["package"],
}, },
Version: version, Version: version,

View File

@ -68,8 +68,7 @@ func DetectFeatures(data map[string][]byte, namespaces []database.Namespace) ([]
} }
// Ensure that every feature has a Namespace associated // Ensure that every feature has a Namespace associated
for i := 0; i < len(pkgs); i++ { for i := 0; i < len(pkgs); i++ {
pkgs[i].Feature.Namespace.Name = namespace.Name pkgs[i].Feature.Namespace = namespace
pkgs[i].Feature.Namespace.Version = namespace.Version
} }
packages = append(packages, pkgs...) packages = append(packages, pkgs...)
break break

View File

@ -76,7 +76,11 @@ func (detector *AptSourcesNamespaceDetector) Detect(data map[string][]byte) *dat
} }
if OS != "" && version != "" { if OS != "" && version != "" {
return &database.Namespace{Name: OS, Version: types.NewVersionUnsafe(version)} if nsVersion, err := types.NewVersion(version); err != nil {
return nil
} else {
return &database.Namespace{Name: OS, Version: nsVersion}
}
} }
return nil return nil
} }

View File

@ -24,7 +24,7 @@ import (
var aptSourcesOSTests = []namespace.NamespaceTest{ var aptSourcesOSTests = []namespace.NamespaceTest{
{ {
ExpectedNamespace: database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("unstable")}, ExpectedNamespace: database.Namespace{Name: "debian", Version: types.NewVersionUnsafe("10")},
Data: map[string][]byte{ Data: map[string][]byte{
"etc/os-release": []byte( "etc/os-release": []byte(
`PRETTY_NAME="Debian GNU/Linux stretch/sid" `PRETTY_NAME="Debian GNU/Linux stretch/sid"

View File

@ -71,7 +71,11 @@ func (detector *LsbReleaseNamespaceDetector) Detect(data map[string][]byte) *dat
} }
if OS != "" && version != "" { if OS != "" && version != "" {
return &database.Namespace{Name: OS, Version: types.NewVersionUnsafe(version)} if nsVersion, err := types.NewVersion(version); err != nil {
return nil
} else {
return &database.Namespace{Name: OS, Version: nsVersion}
}
} }
return nil return nil
} }

View File

@ -66,7 +66,11 @@ func (detector *OsReleaseNamespaceDetector) Detect(data map[string][]byte) *data
} }
if OS != "" && version != "" { if OS != "" && version != "" {
return &database.Namespace{Name: OS, Version: types.NewVersionUnsafe(version)} if nsVersion, err := types.NewVersion(version); err != nil {
return nil
} else {
return &database.Namespace{Name: OS, Version: nsVersion}
}
} }
return nil return nil
} }

View File

@ -47,7 +47,11 @@ func (detector *RedhatReleaseNamespaceDetector) Detect(data map[string][]byte) *
r := redhatReleaseRegexp.FindStringSubmatch(string(f)) r := redhatReleaseRegexp.FindStringSubmatch(string(f))
if len(r) == 4 { if len(r) == 4 {
return &database.Namespace{Name: strings.ToLower(r[1]), Version: types.NewVersionUnsafe(r[3])} if nsVersion, err := types.NewVersion(r[3]); err != nil {
return nil
} else {
return &database.Namespace{Name: strings.ToLower(r[1]), Version: nsVersion}
}
} }
} }