add FixedInVersions to check affected packages
Signed-off-by: liang chenye <liangchenye@huawei.com>
This commit is contained in:
parent
f1498b1d17
commit
cb42892716
@ -144,7 +144,7 @@ Server: clair
|
|||||||
"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",
|
||||||
"FixedBy": "9.23-5"
|
"FixedBy": ">= 9.23-5"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -289,7 +289,7 @@ POST http://localhost:6060/v1/namespaces/debian%3A8/vulnerabilities HTTP/1.1
|
|||||||
{
|
{
|
||||||
"Name": "coreutils",
|
"Name": "coreutils",
|
||||||
"NamespaceName": "debian:8",
|
"NamespaceName": "debian:8",
|
||||||
"Version": "8.23-1"
|
"FixedInVersions": ">= 8.23-1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -322,7 +322,7 @@ Server: clair
|
|||||||
{
|
{
|
||||||
"Name": "coreutils",
|
"Name": "coreutils",
|
||||||
"NamespaceName": "debian:8",
|
"NamespaceName": "debian:8",
|
||||||
"Version": "8.23-1"
|
"FixedInVersions": ">= 8.23-1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -373,7 +373,7 @@ Server: clair
|
|||||||
{
|
{
|
||||||
"Name": "coreutils",
|
"Name": "coreutils",
|
||||||
"NamespaceName": "debian:8",
|
"NamespaceName": "debian:8",
|
||||||
"Version": "8.23-1"
|
"FixedInVersions": ">= 8.23-1"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@ -592,7 +592,7 @@ Server: clair
|
|||||||
{
|
{
|
||||||
"Name": "grep",
|
"Name": "grep",
|
||||||
"NamespaceName": "debian:8",
|
"NamespaceName": "debian:8",
|
||||||
"Version": "2.25"
|
"FixedInVersions": ">= 2.25"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
@ -77,7 +77,7 @@ func LayerFromDatabaseModel(dbLayer database.Layer, withFeatures, withVulnerabil
|
|||||||
Metadata: dbVuln.Metadata,
|
Metadata: dbVuln.Metadata,
|
||||||
}
|
}
|
||||||
|
|
||||||
if dbVuln.FixedBy != types.MaxVersion {
|
if dbVuln.FixedBy.String() != types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion).String() {
|
||||||
vuln.FixedBy = dbVuln.FixedBy.String()
|
vuln.FixedBy = dbVuln.FixedBy.String()
|
||||||
}
|
}
|
||||||
feature.Vulnerabilities = append(feature.Vulnerabilities, vuln)
|
feature.Vulnerabilities = append(feature.Vulnerabilities, vuln)
|
||||||
@ -154,31 +154,32 @@ type Feature struct {
|
|||||||
Name string `json:"Name,omitempty"`
|
Name string `json:"Name,omitempty"`
|
||||||
NamespaceName string `json:"NamespaceName,omitempty"`
|
NamespaceName string `json:"NamespaceName,omitempty"`
|
||||||
Version string `json:"Version,omitempty"`
|
Version string `json:"Version,omitempty"`
|
||||||
|
FixedInVersions string `json:"FixedInVersions,omitempty"`
|
||||||
Vulnerabilities []Vulnerability `json:"Vulnerabilities,omitempty"`
|
Vulnerabilities []Vulnerability `json:"Vulnerabilities,omitempty"`
|
||||||
AddedBy string `json:"AddedBy,omitempty"`
|
AddedBy string `json:"AddedBy,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func FeatureFromDatabaseModel(dbFeatureVersion database.FeatureVersion) Feature {
|
func FeatureFromDatabaseModel(dbFeatureVersion database.FeatureVersion) Feature {
|
||||||
versionStr := dbFeatureVersion.Version.String()
|
fixedInVersionsStr := dbFeatureVersion.FixedInVersions.String()
|
||||||
if versionStr == types.MaxVersion.String() {
|
if fixedInVersionsStr == types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion).String() {
|
||||||
versionStr = "None"
|
fixedInVersionsStr = "None"
|
||||||
}
|
}
|
||||||
|
|
||||||
return Feature{
|
return Feature{
|
||||||
Name: dbFeatureVersion.Feature.Name,
|
Name: dbFeatureVersion.Feature.Name,
|
||||||
NamespaceName: dbFeatureVersion.Feature.Namespace.Name,
|
NamespaceName: dbFeatureVersion.Feature.Namespace.Name,
|
||||||
Version: versionStr,
|
FixedInVersions: fixedInVersionsStr,
|
||||||
AddedBy: dbFeatureVersion.AddedBy.Name,
|
AddedBy: dbFeatureVersion.AddedBy.Name,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (f Feature) DatabaseModel() (database.FeatureVersion, error) {
|
func (f Feature) DatabaseModel() (database.FeatureVersion, error) {
|
||||||
var version types.Version
|
var fivs types.FixedInVersions
|
||||||
if f.Version == "None" {
|
if f.FixedInVersions == "None" {
|
||||||
version = types.MaxVersion
|
fivs = types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion)
|
||||||
} else {
|
} else {
|
||||||
var err error
|
var err error
|
||||||
version, err = types.NewVersion(f.Version)
|
fivs, err = types.NewFixedInVersions(f.FixedInVersions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return database.FeatureVersion{}, err
|
return database.FeatureVersion{}, err
|
||||||
}
|
}
|
||||||
@ -189,7 +190,7 @@ func (f Feature) DatabaseModel() (database.FeatureVersion, error) {
|
|||||||
Name: f.Name,
|
Name: f.Name,
|
||||||
Namespace: database.Namespace{Name: f.NamespaceName},
|
Namespace: database.Namespace{Name: f.NamespaceName},
|
||||||
},
|
},
|
||||||
Version: version,
|
FixedInVersions: fivs,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -53,9 +53,10 @@ type Feature struct {
|
|||||||
type FeatureVersion struct {
|
type FeatureVersion struct {
|
||||||
Model
|
Model
|
||||||
|
|
||||||
Feature Feature
|
Feature Feature
|
||||||
Version types.Version
|
Version types.Version
|
||||||
AffectedBy []Vulnerability
|
FixedInVersions types.FixedInVersions
|
||||||
|
AffectedBy []Vulnerability
|
||||||
|
|
||||||
// For output purposes. Only make sense when the feature version is in the context of an image.
|
// For output purposes. Only make sense when the feature version is in the context of an image.
|
||||||
AddedBy Layer
|
AddedBy Layer
|
||||||
@ -78,7 +79,7 @@ type Vulnerability struct {
|
|||||||
|
|
||||||
// For output purposes. Only make sense when the vulnerability
|
// For output purposes. Only make sense when the vulnerability
|
||||||
// is already about a specific Feature/FeatureVersion.
|
// is already about a specific Feature/FeatureVersion.
|
||||||
FixedBy types.Version `json:",omitempty"`
|
FixedBy types.FixedInVersions `json:",omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type MetadataMap map[string]interface{}
|
type MetadataMap map[string]interface{}
|
||||||
|
@ -85,8 +85,8 @@ func TestRaceAffects(t *testing.T) {
|
|||||||
Namespace: feature.Namespace,
|
Namespace: feature.Namespace,
|
||||||
FixedIn: []database.FeatureVersion{
|
FixedIn: []database.FeatureVersion{
|
||||||
{
|
{
|
||||||
Feature: feature,
|
Feature: feature,
|
||||||
Version: types.NewVersionUnsafe(strconv.Itoa(version)),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">=" + strconv.Itoa(version)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Severity: types.Unknown,
|
Severity: types.Unknown,
|
||||||
@ -149,7 +149,9 @@ func TestRaceAffects(t *testing.T) {
|
|||||||
// Get expected affects.
|
// Get expected affects.
|
||||||
for i := numVulnerabilities; i > featureVersionVersion; i-- {
|
for i := numVulnerabilities; i > featureVersionVersion; i-- {
|
||||||
for _, vulnerability := range vulnerabilities[i] {
|
for _, vulnerability := range vulnerabilities[i] {
|
||||||
expectedAffectedNames = append(expectedAffectedNames, vulnerability.Name)
|
if vulnerability.FixedIn[0].FixedInVersions.Affected(featureVersion.Version) {
|
||||||
|
expectedAffectedNames = append(expectedAffectedNames, vulnerability.Name)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -194,7 +194,7 @@ func (pgSQL *pgSQL) insertFeatureVersions(featureVersions []database.FeatureVers
|
|||||||
type vulnerabilityAffectsFeatureVersion struct {
|
type vulnerabilityAffectsFeatureVersion struct {
|
||||||
vulnerabilityID int
|
vulnerabilityID int
|
||||||
fixedInID int
|
fixedInID int
|
||||||
fixedInVersion types.Version
|
fixedInVersions types.FixedInVersions
|
||||||
}
|
}
|
||||||
|
|
||||||
func linkFeatureVersionToVulnerabilities(tx *sql.Tx, featureVersion database.FeatureVersion) error {
|
func linkFeatureVersionToVulnerabilities(tx *sql.Tx, featureVersion database.FeatureVersion) error {
|
||||||
@ -210,14 +210,12 @@ func linkFeatureVersionToVulnerabilities(tx *sql.Tx, featureVersion database.Fea
|
|||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var affect vulnerabilityAffectsFeatureVersion
|
var affect vulnerabilityAffectsFeatureVersion
|
||||||
|
|
||||||
err := rows.Scan(&affect.fixedInID, &affect.vulnerabilityID, &affect.fixedInVersion)
|
err := rows.Scan(&affect.fixedInID, &affect.vulnerabilityID, &affect.fixedInVersions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return handleError("searchVulnerabilityFixedInFeature.Scan()", err)
|
return handleError("searchVulnerabilityFixedInFeature.Scan()", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if featureVersion.Version.Compare(affect.fixedInVersion) < 0 {
|
if affect.fixedInVersions.Affected(featureVersion.Version) {
|
||||||
// The version of the FeatureVersion we are inserting is lower than the fixed version on this
|
|
||||||
// Vulnerability, thus, this FeatureVersion is affected by it.
|
|
||||||
affects = append(affects, affect)
|
affects = append(affects, affect)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ func TestFindLayer(t *testing.T) {
|
|||||||
assert.Equal(t, types.High, featureVersion.AffectedBy[0].Severity)
|
assert.Equal(t, types.High, featureVersion.AffectedBy[0].Severity)
|
||||||
assert.Equal(t, "A vulnerability affecting OpenSSL < 2.0 on Debian 7.0", featureVersion.AffectedBy[0].Description)
|
assert.Equal(t, "A vulnerability affecting OpenSSL < 2.0 on Debian 7.0", featureVersion.AffectedBy[0].Description)
|
||||||
assert.Equal(t, "http://google.com/#q=CVE-OPENSSL-1-DEB7", featureVersion.AffectedBy[0].Link)
|
assert.Equal(t, "http://google.com/#q=CVE-OPENSSL-1-DEB7", featureVersion.AffectedBy[0].Link)
|
||||||
assert.Equal(t, types.NewVersionUnsafe("2.0"), featureVersion.AffectedBy[0].FixedBy)
|
assert.Equal(t, types.NewFixedInVersionsUnsafe(">= 2.0").String(), featureVersion.AffectedBy[0].FixedBy.String())
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
t.Errorf("unexpected package %s for layer-1", featureVersion.Feature.Name)
|
t.Errorf("unexpected package %s for layer-1", featureVersion.Feature.Name)
|
||||||
|
@ -104,8 +104,8 @@ func TestNotification(t *testing.T) {
|
|||||||
Severity: "Unknown",
|
Severity: "Unknown",
|
||||||
FixedIn: []database.FeatureVersion{
|
FixedIn: []database.FeatureVersion{
|
||||||
{
|
{
|
||||||
Feature: f1,
|
Feature: f1,
|
||||||
Version: types.NewVersionUnsafe("1.0"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.0"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -164,12 +164,12 @@ func TestNotification(t *testing.T) {
|
|||||||
v1b.Severity = types.High
|
v1b.Severity = types.High
|
||||||
v1b.FixedIn = []database.FeatureVersion{
|
v1b.FixedIn = []database.FeatureVersion{
|
||||||
{
|
{
|
||||||
Feature: f1,
|
Feature: f1,
|
||||||
Version: types.MinVersion,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MinVersion),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: f2,
|
Feature: f2,
|
||||||
Version: types.MaxVersion,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
4
database/pgsql/testdata/data.sql
vendored
4
database/pgsql/testdata/data.sql
vendored
@ -48,8 +48,8 @@ INSERT INTO vulnerability (id, namespace_id, name, description, link, severity)
|
|||||||
(2, 1, 'CVE-NOPE', 'A vulnerability affecting nothing', '', 'Unknown');
|
(2, 1, 'CVE-NOPE', 'A vulnerability affecting nothing', '', 'Unknown');
|
||||||
|
|
||||||
INSERT INTO vulnerability_fixedin_feature (id, vulnerability_id, feature_id, version) VALUES
|
INSERT INTO vulnerability_fixedin_feature (id, vulnerability_id, feature_id, version) VALUES
|
||||||
(1, 1, 2, '2.0'),
|
(1, 1, 2, '>= 2.0'),
|
||||||
(2, 1, 4, '1.9-abc');
|
(2, 1, 4, '>= 1.9-abc');
|
||||||
|
|
||||||
INSERT INTO vulnerability_affects_featureversion (id, vulnerability_id, featureversion_id, fixedin_id) VALUES
|
INSERT INTO vulnerability_affects_featureversion (id, vulnerability_id, featureversion_id, fixedin_id) VALUES
|
||||||
(1, 1, 2, 1); -- CVE-OPENSSL-1-DEB7 affects Debian:7 OpenSSL 1.0
|
(1, 1, 2, 1); -- CVE-OPENSSL-1-DEB7 affects Debian:7 OpenSSL 1.0
|
||||||
|
@ -140,11 +140,11 @@ func scanVulnerability(queryer Queryer, queryName string, vulnerabilityRow *sql.
|
|||||||
|
|
||||||
for rows.Next() {
|
for rows.Next() {
|
||||||
var featureVersionID zero.Int
|
var featureVersionID zero.Int
|
||||||
var featureVersionVersion zero.String
|
var featureVersionFixedInVersions zero.String
|
||||||
var featureVersionFeatureName zero.String
|
var featureVersionFeatureName zero.String
|
||||||
|
|
||||||
err := rows.Scan(
|
err := rows.Scan(
|
||||||
&featureVersionVersion,
|
&featureVersionFixedInVersions,
|
||||||
&featureVersionID,
|
&featureVersionID,
|
||||||
&featureVersionFeatureName,
|
&featureVersionFeatureName,
|
||||||
)
|
)
|
||||||
@ -163,7 +163,7 @@ func scanVulnerability(queryer Queryer, queryName string, vulnerabilityRow *sql.
|
|||||||
Namespace: vulnerability.Namespace,
|
Namespace: vulnerability.Namespace,
|
||||||
Name: featureVersionFeatureName.String,
|
Name: featureVersionFeatureName.String,
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe(featureVersionVersion.String),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(featureVersionFixedInVersions.String),
|
||||||
}
|
}
|
||||||
vulnerability.FixedIn = append(vulnerability.FixedIn, featureVersion)
|
vulnerability.FixedIn = append(vulnerability.FixedIn, featureVersion)
|
||||||
}
|
}
|
||||||
@ -274,7 +274,7 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
|
|||||||
// for diffing existing vulnerabilities.
|
// for diffing existing vulnerabilities.
|
||||||
var fixedIn []database.FeatureVersion
|
var fixedIn []database.FeatureVersion
|
||||||
for _, fv := range vulnerability.FixedIn {
|
for _, fv := range vulnerability.FixedIn {
|
||||||
if fv.Version != types.MinVersion {
|
if fv.FixedInVersions.String() != types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MinVersion).String() {
|
||||||
fixedIn = append(fixedIn, fv)
|
fixedIn = append(fixedIn, fv)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -350,7 +350,7 @@ func applyFixedInDiff(currentList, diff []database.FeatureVersion) ([]database.F
|
|||||||
different := false
|
different := false
|
||||||
|
|
||||||
for _, name := range addedNames {
|
for _, name := range addedNames {
|
||||||
if diffMap[name].Version == types.MinVersion {
|
if diffMap[name].FixedInVersions.String() == types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MinVersion).String() {
|
||||||
// MinVersion only makes sense when a Feature is already fixed in some version,
|
// MinVersion only makes sense when a Feature is already fixed in some version,
|
||||||
// in which case we would be in the "inBothNames".
|
// in which case we would be in the "inBothNames".
|
||||||
continue
|
continue
|
||||||
@ -363,7 +363,7 @@ func applyFixedInDiff(currentList, diff []database.FeatureVersion) ([]database.F
|
|||||||
for _, name := range inBothNames {
|
for _, name := range inBothNames {
|
||||||
fv := diffMap[name]
|
fv := diffMap[name]
|
||||||
|
|
||||||
if fv.Version == types.MinVersion {
|
if fv.FixedInVersions.String() == types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MinVersion).String() {
|
||||||
// MinVersion means that the Feature doesn't affect the Vulnerability anymore.
|
// MinVersion means that the Feature doesn't affect the Vulnerability anymore.
|
||||||
delete(currentMap, name)
|
delete(currentMap, name)
|
||||||
different = true
|
different = true
|
||||||
@ -438,7 +438,7 @@ func (pgSQL *pgSQL) insertVulnerabilityFixedInFeatureVersions(tx *sql.Tx, vulner
|
|||||||
err = tx.QueryRow(
|
err = tx.QueryRow(
|
||||||
insertVulnerabilityFixedInFeature,
|
insertVulnerabilityFixedInFeature,
|
||||||
vulnerabilityID, fv.Feature.ID,
|
vulnerabilityID, fv.Feature.ID,
|
||||||
&fv.Version,
|
&fv.FixedInVersions,
|
||||||
).Scan(&fixedInID)
|
).Scan(&fixedInID)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -446,7 +446,7 @@ func (pgSQL *pgSQL) insertVulnerabilityFixedInFeatureVersions(tx *sql.Tx, vulner
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Insert Vulnerability_Affects_FeatureVersion.
|
// Insert Vulnerability_Affects_FeatureVersion.
|
||||||
err = linkVulnerabilityToFeatureVersions(tx, fixedInID, vulnerabilityID, fv.Feature.ID, fv.Version)
|
err = linkVulnerabilityToFeatureVersions(tx, fixedInID, vulnerabilityID, fv.Feature.ID, fv.FixedInVersions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -455,7 +455,7 @@ func (pgSQL *pgSQL) insertVulnerabilityFixedInFeatureVersions(tx *sql.Tx, vulner
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func linkVulnerabilityToFeatureVersions(tx *sql.Tx, fixedInID, vulnerabilityID, featureID int, fixedInVersion types.Version) error {
|
func linkVulnerabilityToFeatureVersions(tx *sql.Tx, fixedInID, vulnerabilityID, featureID int, fixedInVersions types.FixedInVersions) error {
|
||||||
// Find every FeatureVersions of the Feature that the vulnerability affects.
|
// Find every FeatureVersions of the Feature that the vulnerability affects.
|
||||||
// TODO(Quentin-M): LIMIT
|
// TODO(Quentin-M): LIMIT
|
||||||
rows, err := tx.Query(searchFeatureVersionByFeature, featureID)
|
rows, err := tx.Query(searchFeatureVersionByFeature, featureID)
|
||||||
@ -472,10 +472,7 @@ func linkVulnerabilityToFeatureVersions(tx *sql.Tx, fixedInID, vulnerabilityID,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return handleError("searchFeatureVersionByFeature.Scan()", err)
|
return handleError("searchFeatureVersionByFeature.Scan()", err)
|
||||||
}
|
}
|
||||||
|
if fixedInVersions.Affected(affected.Version) {
|
||||||
if affected.Version.Compare(fixedInVersion) < 0 {
|
|
||||||
// The version of the FeatureVersion is lower than the fixed version of this vulnerability,
|
|
||||||
// thus, this FeatureVersion is affected by it.
|
|
||||||
affecteds = append(affecteds, affected)
|
affecteds = append(affecteds, affected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -527,7 +524,7 @@ func (pgSQL *pgSQL) DeleteVulnerabilityFix(vulnerabilityNamespace, vulnerability
|
|||||||
Name: vulnerabilityNamespace,
|
Name: vulnerabilityNamespace,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Version: types.MinVersion,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MinVersion),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
@ -46,12 +46,12 @@ func TestFindVulnerability(t *testing.T) {
|
|||||||
Namespace: database.Namespace{Name: "debian:7"},
|
Namespace: database.Namespace{Name: "debian:7"},
|
||||||
FixedIn: []database.FeatureVersion{
|
FixedIn: []database.FeatureVersion{
|
||||||
{
|
{
|
||||||
Feature: database.Feature{Name: "openssl"},
|
Feature: database.Feature{Name: "openssl"},
|
||||||
Version: types.NewVersionUnsafe("2.0"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 2.0"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: database.Feature{Name: "libssl"},
|
Feature: database.Feature{Name: "libssl"},
|
||||||
Version: types.NewVersionUnsafe("1.9-abc"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.9-abc"),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@ -114,50 +114,50 @@ func TestInsertVulnerability(t *testing.T) {
|
|||||||
Name: "TestInsertVulnerabilityFeatureVersion1",
|
Name: "TestInsertVulnerabilityFeatureVersion1",
|
||||||
Namespace: n1,
|
Namespace: n1,
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("1.0"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.0"),
|
||||||
}
|
}
|
||||||
f2 := database.FeatureVersion{
|
f2 := database.FeatureVersion{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Name: "TestInsertVulnerabilityFeatureVersion1",
|
Name: "TestInsertVulnerabilityFeatureVersion1",
|
||||||
Namespace: n2,
|
Namespace: n2,
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("1.0"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.0"),
|
||||||
}
|
}
|
||||||
f3 := database.FeatureVersion{
|
f3 := database.FeatureVersion{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Name: "TestInsertVulnerabilityFeatureVersion2",
|
Name: "TestInsertVulnerabilityFeatureVersion2",
|
||||||
},
|
},
|
||||||
Version: types.MaxVersion,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion),
|
||||||
}
|
}
|
||||||
f4 := database.FeatureVersion{
|
f4 := database.FeatureVersion{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Name: "TestInsertVulnerabilityFeatureVersion2",
|
Name: "TestInsertVulnerabilityFeatureVersion2",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("1.4"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.4"),
|
||||||
}
|
}
|
||||||
f5 := database.FeatureVersion{
|
f5 := database.FeatureVersion{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Name: "TestInsertVulnerabilityFeatureVersion3",
|
Name: "TestInsertVulnerabilityFeatureVersion3",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("1.5"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.5"),
|
||||||
}
|
}
|
||||||
f6 := database.FeatureVersion{
|
f6 := database.FeatureVersion{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Name: "TestInsertVulnerabilityFeatureVersion4",
|
Name: "TestInsertVulnerabilityFeatureVersion4",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("0.1"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.1"),
|
||||||
}
|
}
|
||||||
f7 := database.FeatureVersion{
|
f7 := database.FeatureVersion{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Name: "TestInsertVulnerabilityFeatureVersion5",
|
Name: "TestInsertVulnerabilityFeatureVersion5",
|
||||||
},
|
},
|
||||||
Version: types.MaxVersion,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion),
|
||||||
}
|
}
|
||||||
f8 := database.FeatureVersion{
|
f8 := database.FeatureVersion{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Name: "TestInsertVulnerabilityFeatureVersion5",
|
Name: "TestInsertVulnerabilityFeatureVersion5",
|
||||||
},
|
},
|
||||||
Version: types.MinVersion,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MinVersion),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert invalid vulnerabilities.
|
// Insert invalid vulnerabilities.
|
||||||
|
@ -196,7 +196,7 @@ func parseDebianJSON(data *jsonData) (vulnerabilities []database.Vulnerability,
|
|||||||
Name: "debian:" + database.DebianReleasesMapping[releaseName],
|
Name: "debian:" + database.DebianReleasesMapping[releaseName],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Version: version,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, version),
|
||||||
}
|
}
|
||||||
vulnerability.FixedIn = append(vulnerability.FixedIn, pkg)
|
vulnerability.FixedIn = append(vulnerability.FixedIn, pkg)
|
||||||
|
|
||||||
|
@ -45,7 +45,7 @@ func TestDebianParser(t *testing.T) {
|
|||||||
Namespace: database.Namespace{Name: "debian:8"},
|
Namespace: database.Namespace{Name: "debian:8"},
|
||||||
Name: "aptdaemon",
|
Name: "aptdaemon",
|
||||||
},
|
},
|
||||||
Version: types.MaxVersion,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
@ -53,7 +53,7 @@ func TestDebianParser(t *testing.T) {
|
|||||||
|
|
||||||
Name: "aptdaemon",
|
Name: "aptdaemon",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("1.1.1+bzr982-1"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.1.1+bzr982-1"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -71,21 +71,21 @@ func TestDebianParser(t *testing.T) {
|
|||||||
Namespace: database.Namespace{Name: "debian:8"},
|
Namespace: database.Namespace{Name: "debian:8"},
|
||||||
Name: "aptdaemon",
|
Name: "aptdaemon",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("0.7.0"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.7.0"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Namespace: database.Namespace{Name: "debian:unstable"},
|
Namespace: database.Namespace{Name: "debian:unstable"},
|
||||||
Name: "aptdaemon",
|
Name: "aptdaemon",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("0.7.0"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.7.0"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Namespace: database.Namespace{Name: "debian:8"},
|
Namespace: database.Namespace{Name: "debian:8"},
|
||||||
Name: "asterisk",
|
Name: "asterisk",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("0.5.56"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.5.56"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -103,7 +103,7 @@ func TestDebianParser(t *testing.T) {
|
|||||||
Namespace: database.Namespace{Name: "debian:8"},
|
Namespace: database.Namespace{Name: "debian:8"},
|
||||||
Name: "asterisk",
|
Name: "asterisk",
|
||||||
},
|
},
|
||||||
Version: types.MinVersion,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MinVersion),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,8 +31,6 @@ const (
|
|||||||
cveURLPrefix = "http://cve.mitre.org/cgi-bin/cvename.cgi?name="
|
cveURLPrefix = "http://cve.mitre.org/cgi-bin/cvename.cgi?name="
|
||||||
updaterFlag = "nodejsUpdater"
|
updaterFlag = "nodejsUpdater"
|
||||||
defaultNodejsVersion = "all"
|
defaultNodejsVersion = "all"
|
||||||
//Add a suffix when an advisory is fixed `after` a certain version.
|
|
||||||
defaultVersionSuffix = "-1"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = capnslog.NewPackageLogger("github.com/coreos/clair", "updater/fetchers/nodejs")
|
var log = capnslog.NewPackageLogger("github.com/coreos/clair", "updater/fetchers/nodejs")
|
||||||
@ -131,8 +129,10 @@ func parseNodejsAdvisories(advisories []nodejsAdvisory, latestUpdate string) (vu
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
if version, err := getAdvisoryVersion(advisory.PatchedVersions); err == nil {
|
if fivs, err := types.NewFixedInVersions(advisory.PatchedVersions); err == nil {
|
||||||
pkg.Version = version
|
pkg.FixedInVersions = fivs
|
||||||
|
} else {
|
||||||
|
log.Warningf("could not parse nodejs patched version: '%s'.", err)
|
||||||
}
|
}
|
||||||
vulnerability.FixedIn = append(vulnerability.FixedIn, pkg)
|
vulnerability.FixedIn = append(vulnerability.FixedIn, pkg)
|
||||||
|
|
||||||
@ -149,34 +149,5 @@ func parseNodejsAdvisories(advisories []nodejsAdvisory, latestUpdate string) (vu
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// getAdvisoryVersion parses a string containing one or multiple version ranges
|
|
||||||
// and returns upper-bound. By nature, this simplification may lead to false-positives
|
|
||||||
func getAdvisoryVersion(fullVersion string) (types.Version, error) {
|
|
||||||
fixedVersion := types.MinVersion
|
|
||||||
|
|
||||||
for _, version := range strings.Split(fullVersion, "||") {
|
|
||||||
ovs := getOperVersions(version)
|
|
||||||
for _, ov := range ovs {
|
|
||||||
if ov.Oper == ">" {
|
|
||||||
if curVersion, err := types.NewVersion(ov.Version + defaultVersionSuffix); err != nil {
|
|
||||||
log.Warningf("could not parse package version '%s': %s. skipping", curVersion, err.Error())
|
|
||||||
} else if curVersion.Compare(fixedVersion) > 0 {
|
|
||||||
fixedVersion = curVersion
|
|
||||||
}
|
|
||||||
} else if ov.Oper == ">=" {
|
|
||||||
if curVersion, err := types.NewVersion(ov.Version); err != nil {
|
|
||||||
log.Warningf("could not parse package version '%s': %s. skipping", curVersion, err.Error())
|
|
||||||
} else if curVersion.Compare(fixedVersion) > 0 {
|
|
||||||
fixedVersion = curVersion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if fixedVersion != types.MinVersion {
|
|
||||||
return fixedVersion, nil
|
|
||||||
}
|
|
||||||
return types.MaxVersion, cerrors.ErrNotFound
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clean deletes any allocated resources.
|
// Clean deletes any allocated resources.
|
||||||
func (fetcher *NodejsFetcher) Clean() {}
|
func (fetcher *NodejsFetcher) Clean() {}
|
||||||
|
@ -50,14 +50,14 @@ func TestNodejsParser(t *testing.T) {
|
|||||||
Namespace: database.Namespace{Name: "nodejs:" + defaultNodejsVersion},
|
Namespace: database.Namespace{Name: "nodejs:" + defaultNodejsVersion},
|
||||||
Name: "ldapauth",
|
Name: "ldapauth",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("2.2.4" + defaultVersionSuffix),
|
FixedInVersions: types.NewFixedInVersionsUnsafe("> 2.2.4"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Namespace: database.Namespace{Name: "nodejs:" + defaultNodejsVersion},
|
Namespace: database.Namespace{Name: "nodejs:" + defaultNodejsVersion},
|
||||||
Name: "ldapauth-fork",
|
Name: "ldapauth-fork",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("2.3.3"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 2.3.3"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, expectedFeatureVersion := range expectedFeatureVersions {
|
for _, expectedFeatureVersion := range expectedFeatureVersions {
|
||||||
@ -73,7 +73,7 @@ func TestNodejsParser(t *testing.T) {
|
|||||||
Namespace: database.Namespace{Name: "nodejs:" + defaultNodejsVersion},
|
Namespace: database.Namespace{Name: "nodejs:" + defaultNodejsVersion},
|
||||||
Name: "hawk",
|
Name: "hawk",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("4.1.1"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">=3.1.3 < 4.0.0 || >=4.1.1"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
for _, expectedFeatureVersion := range expectedFeatureVersions {
|
for _, expectedFeatureVersion := range expectedFeatureVersions {
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
// Copyright 2016 clair authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package nodejs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"unicode"
|
|
||||||
)
|
|
||||||
|
|
||||||
type operVersion struct {
|
|
||||||
Oper string
|
|
||||||
Version string
|
|
||||||
}
|
|
||||||
|
|
||||||
type ovState string
|
|
||||||
|
|
||||||
const (
|
|
||||||
ovStateInit ovState = "init"
|
|
||||||
ovStateOper ovState = "operation"
|
|
||||||
ovStateVersion ovState = "version"
|
|
||||||
)
|
|
||||||
|
|
||||||
func isOper(ch rune) bool {
|
|
||||||
return ch == '>' || ch == '<' || ch == '='
|
|
||||||
}
|
|
||||||
|
|
||||||
func getOperVersions(content string) (ovs []operVersion) {
|
|
||||||
state := ovStateInit
|
|
||||||
begin := 0
|
|
||||||
var ov operVersion
|
|
||||||
for i, ch := range content {
|
|
||||||
if unicode.IsSpace(ch) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
switch state {
|
|
||||||
case ovStateInit:
|
|
||||||
if isOper(ch) {
|
|
||||||
state = ovStateOper
|
|
||||||
begin = i
|
|
||||||
} else {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
case ovStateOper:
|
|
||||||
if !isOper(ch) {
|
|
||||||
state = ovStateVersion
|
|
||||||
ov.Oper = strings.TrimSpace(content[begin:i])
|
|
||||||
begin = i
|
|
||||||
}
|
|
||||||
case ovStateVersion:
|
|
||||||
if isOper(ch) {
|
|
||||||
state = ovStateOper
|
|
||||||
ov.Version = strings.TrimSpace(content[begin:i])
|
|
||||||
ovs = append(ovs, ov)
|
|
||||||
begin = i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if state == ovStateVersion {
|
|
||||||
ov.Version = strings.TrimSpace(content[begin:len(content)])
|
|
||||||
ovs = append(ovs, ov)
|
|
||||||
}
|
|
||||||
|
|
||||||
return
|
|
||||||
}
|
|
@ -1,53 +0,0 @@
|
|||||||
// Copyright 2016 clair authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package nodejs
|
|
||||||
|
|
||||||
import (
|
|
||||||
"strings"
|
|
||||||
"testing"
|
|
||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
|
||||||
)
|
|
||||||
|
|
||||||
func TestNodeVersion(t *testing.T) {
|
|
||||||
invalid_version := "3.1.3 < 4.0.0 || >= "
|
|
||||||
versions := strings.Split(invalid_version, "||")
|
|
||||||
for _, version := range versions {
|
|
||||||
ovs := getOperVersions(version)
|
|
||||||
assert.Len(t, ovs, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
valid_version := ">=3.1.3 < 4.0.0 || >=4.1.1"
|
|
||||||
versions = strings.Split(valid_version, "||")
|
|
||||||
for _, version := range versions {
|
|
||||||
if strings.Contains(version, "4.1.1") {
|
|
||||||
ovs := getOperVersions(version)
|
|
||||||
assert.Len(t, ovs, 1)
|
|
||||||
assert.Equal(t, ">=", ovs[0].Oper)
|
|
||||||
assert.Equal(t, "4.1.1", ovs[0].Version)
|
|
||||||
} else {
|
|
||||||
ovs := getOperVersions(version)
|
|
||||||
assert.Len(t, ovs, 2)
|
|
||||||
|
|
||||||
for _, ov := range ovs {
|
|
||||||
if ov.Oper == ">=" {
|
|
||||||
assert.Equal(t, "3.1.3", ov.Version)
|
|
||||||
} else if ov.Oper == "<" {
|
|
||||||
assert.Equal(t, "4.0.0", ov.Version)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -283,11 +283,13 @@ func toFeatureVersions(criteria criteria) []database.FeatureVersion {
|
|||||||
}
|
}
|
||||||
} else if strings.Contains(c.Comment, " is earlier than ") {
|
} else if strings.Contains(c.Comment, " is earlier than ") {
|
||||||
const prefixLen = len(" is earlier than ")
|
const prefixLen = len(" is earlier than ")
|
||||||
|
var version types.Version
|
||||||
featureVersion.Feature.Name = strings.TrimSpace(c.Comment[:strings.Index(c.Comment, " is earlier than ")])
|
featureVersion.Feature.Name = strings.TrimSpace(c.Comment[:strings.Index(c.Comment, " is earlier than ")])
|
||||||
featureVersion.Version, err = types.NewVersion(c.Comment[strings.Index(c.Comment, " is earlier than ")+prefixLen:])
|
version, err = types.NewVersion(c.Comment[strings.Index(c.Comment, " is earlier than ")+prefixLen:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
log.Warningf("could not parse package version '%s': %s. skipping", c.Comment[strings.Index(c.Comment, " is earlier than ")+prefixLen:], err.Error())
|
log.Warningf("could not parse package version '%s': %s. skipping", c.Comment[strings.Index(c.Comment, " is earlier than ")+prefixLen:], err.Error())
|
||||||
}
|
}
|
||||||
|
featureVersion.FixedInVersions = types.NewFixedInVersionsFromOV(types.OpGreaterEqual, version)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -297,7 +299,7 @@ func toFeatureVersions(criteria criteria) []database.FeatureVersion {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
if featureVersion.Feature.Namespace.Name != "" && featureVersion.Feature.Name != "" && featureVersion.Version.String() != "" {
|
if featureVersion.Feature.Namespace.Name != "" && featureVersion.Feature.Name != "" && featureVersion.FixedInVersions.String() != "" {
|
||||||
featureVersionParameters[featureVersion.Feature.Namespace.Name+":"+featureVersion.Feature.Name] = featureVersion
|
featureVersionParameters[featureVersion.Feature.Namespace.Name+":"+featureVersion.Feature.Name] = featureVersion
|
||||||
} else {
|
} else {
|
||||||
log.Warningf("could not determine a valid package from criterions: %v", criterions)
|
log.Warningf("could not determine a valid package from criterions: %v", criterions)
|
||||||
|
@ -45,21 +45,21 @@ func TestRHELParser(t *testing.T) {
|
|||||||
Namespace: database.Namespace{Name: "centos:7"},
|
Namespace: database.Namespace{Name: "centos:7"},
|
||||||
Name: "xerces-c",
|
Name: "xerces-c",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("3.1.1-7.el7_1"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 3.1.1-7.el7_1"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Namespace: database.Namespace{Name: "centos:7"},
|
Namespace: database.Namespace{Name: "centos:7"},
|
||||||
Name: "xerces-c-devel",
|
Name: "xerces-c-devel",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("3.1.1-7.el7_1"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 3.1.1-7.el7_1"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Namespace: database.Namespace{Name: "centos:7"},
|
Namespace: database.Namespace{Name: "centos:7"},
|
||||||
Name: "xerces-c-doc",
|
Name: "xerces-c-doc",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("3.1.1-7.el7_1"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 3.1.1-7.el7_1"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,14 +84,14 @@ func TestRHELParser(t *testing.T) {
|
|||||||
Namespace: database.Namespace{Name: "centos:6"},
|
Namespace: database.Namespace{Name: "centos:6"},
|
||||||
Name: "firefox",
|
Name: "firefox",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("38.1.0-1.el6_6"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 38.1.0-1.el6_6"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Namespace: database.Namespace{Name: "centos:7"},
|
Namespace: database.Namespace{Name: "centos:7"},
|
||||||
Name: "firefox",
|
Name: "firefox",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("38.1.0-1.el7_1"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 38.1.0-1.el7_1"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -368,7 +368,7 @@ func parseUbuntuCVE(fileContent io.Reader) (vulnerability database.Vulnerability
|
|||||||
Namespace: database.Namespace{Name: "ubuntu:" + database.UbuntuReleasesMapping[md["release"]]},
|
Namespace: database.Namespace{Name: "ubuntu:" + database.UbuntuReleasesMapping[md["release"]]},
|
||||||
Name: md["package"],
|
Name: md["package"],
|
||||||
},
|
},
|
||||||
Version: version,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, version),
|
||||||
}
|
}
|
||||||
vulnerability.FixedIn = append(vulnerability.FixedIn, featureVersion)
|
vulnerability.FixedIn = append(vulnerability.FixedIn, featureVersion)
|
||||||
}
|
}
|
||||||
|
@ -48,21 +48,21 @@ func TestUbuntuParser(t *testing.T) {
|
|||||||
Namespace: database.Namespace{Name: "ubuntu:14.04"},
|
Namespace: database.Namespace{Name: "ubuntu:14.04"},
|
||||||
Name: "libmspack",
|
Name: "libmspack",
|
||||||
},
|
},
|
||||||
Version: types.MaxVersion,
|
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Namespace: database.Namespace{Name: "ubuntu:15.04"},
|
Namespace: database.Namespace{Name: "ubuntu:15.04"},
|
||||||
Name: "libmspack",
|
Name: "libmspack",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("0.4-3"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.4-3"),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Feature: database.Feature{
|
Feature: database.Feature{
|
||||||
Namespace: database.Namespace{Name: "ubuntu:15.10"},
|
Namespace: database.Namespace{Name: "ubuntu:15.10"},
|
||||||
Name: "libmspack-anotherpkg",
|
Name: "libmspack-anotherpkg",
|
||||||
},
|
},
|
||||||
Version: types.NewVersionUnsafe("0.1"),
|
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.1"),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
237
utils/types/fixedin_versions.go
Normal file
237
utils/types/fixedin_versions.go
Normal file
@ -0,0 +1,237 @@
|
|||||||
|
// Copyright 2016 clair authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql/driver"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"unicode"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Operator string
|
||||||
|
|
||||||
|
const (
|
||||||
|
OpNotEqual Operator = "!="
|
||||||
|
OpLessThan Operator = "<"
|
||||||
|
OpLessEqual Operator = "<="
|
||||||
|
OpEqualTo Operator = "=="
|
||||||
|
OpGreaterEqual Operator = ">="
|
||||||
|
OpGreaterThan Operator = ">"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FixedInVersions struct {
|
||||||
|
fivs [][]operVersion
|
||||||
|
}
|
||||||
|
|
||||||
|
type operVersion struct {
|
||||||
|
oper Operator
|
||||||
|
version Version
|
||||||
|
}
|
||||||
|
|
||||||
|
type ovState string
|
||||||
|
|
||||||
|
const (
|
||||||
|
ovStateInit ovState = "init"
|
||||||
|
ovStateOper ovState = "operation"
|
||||||
|
ovStateVersion ovState = "version"
|
||||||
|
)
|
||||||
|
|
||||||
|
func isOperChar(ch rune) bool {
|
||||||
|
return ch == '>' || ch == '<' || ch == '='
|
||||||
|
}
|
||||||
|
|
||||||
|
func getOperator(str string) (oper Operator, error error) {
|
||||||
|
switch str {
|
||||||
|
case "!=":
|
||||||
|
case "<":
|
||||||
|
case "<=":
|
||||||
|
case "==":
|
||||||
|
case ">=":
|
||||||
|
case ">":
|
||||||
|
default:
|
||||||
|
return oper, fmt.Errorf("Invalid operator: '%s'", str)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Operator(str), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFixedinVersion(content string) (ovs []operVersion, err error) {
|
||||||
|
state := ovStateInit
|
||||||
|
begin := 0
|
||||||
|
var ov operVersion
|
||||||
|
for i, ch := range content {
|
||||||
|
if unicode.IsSpace(ch) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
switch state {
|
||||||
|
case ovStateInit:
|
||||||
|
if isOperChar(ch) {
|
||||||
|
state = ovStateOper
|
||||||
|
} else {
|
||||||
|
// Default to '>='
|
||||||
|
ov.oper = OpGreaterEqual
|
||||||
|
state = ovStateVersion
|
||||||
|
}
|
||||||
|
begin = i
|
||||||
|
case ovStateOper:
|
||||||
|
if !isOperChar(ch) {
|
||||||
|
state = ovStateVersion
|
||||||
|
if ov.oper, err = getOperator(strings.TrimSpace(content[begin:i])); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
begin = i
|
||||||
|
}
|
||||||
|
case ovStateVersion:
|
||||||
|
if isOperChar(ch) {
|
||||||
|
state = ovStateOper
|
||||||
|
if ov.version, err = NewVersion(strings.TrimSpace(content[begin:i])); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ovs = append(ovs, ov)
|
||||||
|
begin = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if state == ovStateVersion {
|
||||||
|
if ov.version, err = NewVersion(strings.TrimSpace(content[begin:len(content)])); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ovs = append(ovs, ov)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(ovs) == 0 {
|
||||||
|
err = fmt.Errorf("Failed to parse '%s'", content)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ov operVersion) patched(version Version) bool {
|
||||||
|
val := version.Compare(ov.version)
|
||||||
|
switch ov.oper {
|
||||||
|
case OpNotEqual:
|
||||||
|
return val != 0
|
||||||
|
case OpLessThan:
|
||||||
|
return val < 0
|
||||||
|
case OpLessEqual:
|
||||||
|
return val <= 0
|
||||||
|
case OpEqualTo:
|
||||||
|
return val == 0
|
||||||
|
case OpGreaterEqual:
|
||||||
|
return val >= 0
|
||||||
|
case OpGreaterThan:
|
||||||
|
return val > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
//Cannot get here
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// String returns the string representation of a FixedInVersions
|
||||||
|
func (fivs FixedInVersions) String() (s string) {
|
||||||
|
firstFiv := false
|
||||||
|
for _, fiv := range fivs.fivs {
|
||||||
|
if !firstFiv {
|
||||||
|
firstFiv = true
|
||||||
|
} else {
|
||||||
|
s += " || "
|
||||||
|
}
|
||||||
|
|
||||||
|
firstOV := false
|
||||||
|
for _, ov := range fiv {
|
||||||
|
if !firstOV {
|
||||||
|
firstOV = true
|
||||||
|
} else {
|
||||||
|
s += " "
|
||||||
|
}
|
||||||
|
s += string(ov.oper) + ov.version.String()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fivs FixedInVersions) MarshalJSON() ([]byte, error) {
|
||||||
|
return json.Marshal(fivs.String())
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fivs *FixedInVersions) UnmarshalJSON(b []byte) (err error) {
|
||||||
|
var str string
|
||||||
|
json.Unmarshal(b, &str)
|
||||||
|
vp := NewFixedInVersionsUnsafe(str)
|
||||||
|
*fivs = vp
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fivs *FixedInVersions) Scan(value interface{}) (err error) {
|
||||||
|
val, ok := value.([]byte)
|
||||||
|
if !ok {
|
||||||
|
return errors.New("could not scan a Version from a non-string input")
|
||||||
|
}
|
||||||
|
*fivs, err = NewFixedInVersions(string(val))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fivs *FixedInVersions) Value() (driver.Value, error) {
|
||||||
|
return fivs.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (fivs FixedInVersions) Affected(version Version) bool {
|
||||||
|
for _, fiv := range fivs.fivs {
|
||||||
|
affected := false
|
||||||
|
for _, ov := range fiv {
|
||||||
|
if !ov.patched(version) {
|
||||||
|
affected = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if !affected {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFixedInVersionsFromOV(oper Operator, version Version) FixedInVersions {
|
||||||
|
var fivs FixedInVersions
|
||||||
|
var fiv []operVersion
|
||||||
|
|
||||||
|
fiv = append(fiv, operVersion{oper, version})
|
||||||
|
fivs.fivs = append(fivs.fivs, fiv)
|
||||||
|
|
||||||
|
return fivs
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFixedInVersions(str string) (FixedInVersions, error) {
|
||||||
|
var fivs FixedInVersions
|
||||||
|
for _, ovsStr := range strings.Split(str, "||") {
|
||||||
|
if fiv, err := getFixedinVersion(ovsStr); err == nil {
|
||||||
|
fivs.fivs = append(fivs.fivs, fiv)
|
||||||
|
} else {
|
||||||
|
return fivs, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fivs, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewFixedInVersionsUnsafe(str string) FixedInVersions {
|
||||||
|
fivs, _ := NewFixedInVersions(str)
|
||||||
|
return fivs
|
||||||
|
}
|
69
utils/types/fixedin_versions_test.go
Normal file
69
utils/types/fixedin_versions_test.go
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
// Copyright 2016 clair authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
// you may not use this file except in compliance with the License.
|
||||||
|
// You may obtain a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
// See the License for the specific language governing permissions and
|
||||||
|
// limitations under the License.
|
||||||
|
|
||||||
|
package types
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestFixedInVersions(t *testing.T) {
|
||||||
|
invalid_version := "3.1.3 < 4.0.0 || >= "
|
||||||
|
fivs, err := NewFixedInVersions(invalid_version)
|
||||||
|
assert.Error(t, err, "Failed to parse '%s'", ">=")
|
||||||
|
|
||||||
|
invalid_version = "3.1.3 < ab.0.0 || >= "
|
||||||
|
fivs, err = NewFixedInVersions(invalid_version)
|
||||||
|
assert.Error(t, err, "Failed to parse '%s'", ">=")
|
||||||
|
|
||||||
|
valid_version := "3.1.3"
|
||||||
|
fivs, err = NewFixedInVersions(valid_version)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
|
||||||
|
valid_version = ">=3.1.3 <4.0.0 || >=4.1.1"
|
||||||
|
fivs, err = NewFixedInVersions(valid_version)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
assert.Equal(t, strings.Replace(fivs.String(), " ", "", -1), strings.Replace(valid_version, " ", "", -1))
|
||||||
|
|
||||||
|
for _, fiv := range fivs.fivs {
|
||||||
|
if len(fiv) == 1 {
|
||||||
|
assert.Equal(t, OpGreaterEqual, fiv[0].oper)
|
||||||
|
assert.Equal(t, NewVersionUnsafe("4.1.1"), fiv[0].version)
|
||||||
|
} else {
|
||||||
|
for _, ov := range fiv {
|
||||||
|
if ov.oper == OpGreaterEqual {
|
||||||
|
assert.Equal(t, NewVersionUnsafe("3.1.3"), ov.version)
|
||||||
|
} else if ov.oper == OpLessThan {
|
||||||
|
assert.Equal(t, NewVersionUnsafe("4.0.0"), ov.version)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cases := []struct {
|
||||||
|
version string
|
||||||
|
expected bool
|
||||||
|
}{
|
||||||
|
{"4.2", false},
|
||||||
|
{"4.0.0", true},
|
||||||
|
{"3.1.3", false},
|
||||||
|
{"3.1.2", true},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
assert.Equal(t, fivs.Affected(NewVersionUnsafe(c.version)), c.expected)
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user