add FixedInVersions to check affected packages

Signed-off-by: liang chenye <liangchenye@huawei.com>
pull/175/head
liang chenye 8 years ago
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.",
"Link": "https://security-tracker.debian.org/tracker/CVE-2014-9471",
"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",
"NamespaceName": "debian:8",
"Version": "8.23-1"
"FixedInVersions": ">= 8.23-1"
}
]
}
@ -322,7 +322,7 @@ Server: clair
{
"Name": "coreutils",
"NamespaceName": "debian:8",
"Version": "8.23-1"
"FixedInVersions": ">= 8.23-1"
}
]
}
@ -373,7 +373,7 @@ Server: clair
{
"Name": "coreutils",
"NamespaceName": "debian:8",
"Version": "8.23-1"
"FixedInVersions": ">= 8.23-1"
}
]
}
@ -592,7 +592,7 @@ Server: clair
{
"Name": "grep",
"NamespaceName": "debian:8",
"Version": "2.25"
"FixedInVersions": ">= 2.25"
}
]
},

@ -77,7 +77,7 @@ func LayerFromDatabaseModel(dbLayer database.Layer, withFeatures, withVulnerabil
Metadata: dbVuln.Metadata,
}
if dbVuln.FixedBy != types.MaxVersion {
if dbVuln.FixedBy.String() != types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion).String() {
vuln.FixedBy = dbVuln.FixedBy.String()
}
feature.Vulnerabilities = append(feature.Vulnerabilities, vuln)
@ -154,31 +154,32 @@ type Feature struct {
Name string `json:"Name,omitempty"`
NamespaceName string `json:"NamespaceName,omitempty"`
Version string `json:"Version,omitempty"`
FixedInVersions string `json:"FixedInVersions,omitempty"`
Vulnerabilities []Vulnerability `json:"Vulnerabilities,omitempty"`
AddedBy string `json:"AddedBy,omitempty"`
}
func FeatureFromDatabaseModel(dbFeatureVersion database.FeatureVersion) Feature {
versionStr := dbFeatureVersion.Version.String()
if versionStr == types.MaxVersion.String() {
versionStr = "None"
fixedInVersionsStr := dbFeatureVersion.FixedInVersions.String()
if fixedInVersionsStr == types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion).String() {
fixedInVersionsStr = "None"
}
return Feature{
Name: dbFeatureVersion.Feature.Name,
NamespaceName: dbFeatureVersion.Feature.Namespace.Name,
Version: versionStr,
AddedBy: dbFeatureVersion.AddedBy.Name,
Name: dbFeatureVersion.Feature.Name,
NamespaceName: dbFeatureVersion.Feature.Namespace.Name,
FixedInVersions: fixedInVersionsStr,
AddedBy: dbFeatureVersion.AddedBy.Name,
}
}
func (f Feature) DatabaseModel() (database.FeatureVersion, error) {
var version types.Version
if f.Version == "None" {
version = types.MaxVersion
var fivs types.FixedInVersions
if f.FixedInVersions == "None" {
fivs = types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion)
} else {
var err error
version, err = types.NewVersion(f.Version)
fivs, err = types.NewFixedInVersions(f.FixedInVersions)
if err != nil {
return database.FeatureVersion{}, err
}
@ -189,7 +190,7 @@ func (f Feature) DatabaseModel() (database.FeatureVersion, error) {
Name: f.Name,
Namespace: database.Namespace{Name: f.NamespaceName},
},
Version: version,
FixedInVersions: fivs,
}, nil
}

@ -53,9 +53,10 @@ type Feature struct {
type FeatureVersion struct {
Model
Feature Feature
Version types.Version
AffectedBy []Vulnerability
Feature Feature
Version types.Version
FixedInVersions types.FixedInVersions
AffectedBy []Vulnerability
// For output purposes. Only make sense when the feature version is in the context of an image.
AddedBy Layer
@ -78,7 +79,7 @@ type Vulnerability struct {
// For output purposes. Only make sense when the vulnerability
// is already about a specific Feature/FeatureVersion.
FixedBy types.Version `json:",omitempty"`
FixedBy types.FixedInVersions `json:",omitempty"`
}
type MetadataMap map[string]interface{}

@ -85,8 +85,8 @@ func TestRaceAffects(t *testing.T) {
Namespace: feature.Namespace,
FixedIn: []database.FeatureVersion{
{
Feature: feature,
Version: types.NewVersionUnsafe(strconv.Itoa(version)),
Feature: feature,
FixedInVersions: types.NewFixedInVersionsUnsafe(">=" + strconv.Itoa(version)),
},
},
Severity: types.Unknown,
@ -149,7 +149,9 @@ func TestRaceAffects(t *testing.T) {
// Get expected affects.
for i := numVulnerabilities; i > featureVersionVersion; 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 {
vulnerabilityID int
fixedInID int
fixedInVersion types.Version
fixedInVersions types.FixedInVersions
}
func linkFeatureVersionToVulnerabilities(tx *sql.Tx, featureVersion database.FeatureVersion) error {
@ -210,14 +210,12 @@ func linkFeatureVersionToVulnerabilities(tx *sql.Tx, featureVersion database.Fea
for rows.Next() {
var affect vulnerabilityAffectsFeatureVersion
err := rows.Scan(&affect.fixedInID, &affect.vulnerabilityID, &affect.fixedInVersion)
err := rows.Scan(&affect.fixedInID, &affect.vulnerabilityID, &affect.fixedInVersions)
if err != nil {
return handleError("searchVulnerabilityFixedInFeature.Scan()", err)
}
if featureVersion.Version.Compare(affect.fixedInVersion) < 0 {
// The version of the FeatureVersion we are inserting is lower than the fixed version on this
// Vulnerability, thus, this FeatureVersion is affected by it.
if affect.fixedInVersions.Affected(featureVersion.Version) {
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, "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, types.NewVersionUnsafe("2.0"), featureVersion.AffectedBy[0].FixedBy)
assert.Equal(t, types.NewFixedInVersionsUnsafe(">= 2.0").String(), featureVersion.AffectedBy[0].FixedBy.String())
}
default:
t.Errorf("unexpected package %s for layer-1", featureVersion.Feature.Name)

@ -104,8 +104,8 @@ func TestNotification(t *testing.T) {
Severity: "Unknown",
FixedIn: []database.FeatureVersion{
{
Feature: f1,
Version: types.NewVersionUnsafe("1.0"),
Feature: f1,
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.0"),
},
},
}
@ -164,12 +164,12 @@ func TestNotification(t *testing.T) {
v1b.Severity = types.High
v1b.FixedIn = []database.FeatureVersion{
{
Feature: f1,
Version: types.MinVersion,
Feature: f1,
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MinVersion),
},
{
Feature: f2,
Version: types.MaxVersion,
Feature: f2,
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion),
},
}

@ -48,8 +48,8 @@ INSERT INTO vulnerability (id, namespace_id, name, description, link, severity)
(2, 1, 'CVE-NOPE', 'A vulnerability affecting nothing', '', 'Unknown');
INSERT INTO vulnerability_fixedin_feature (id, vulnerability_id, feature_id, version) VALUES
(1, 1, 2, '2.0'),
(2, 1, 4, '1.9-abc');
(1, 1, 2, '>= 2.0'),
(2, 1, 4, '>= 1.9-abc');
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

@ -140,11 +140,11 @@ func scanVulnerability(queryer Queryer, queryName string, vulnerabilityRow *sql.
for rows.Next() {
var featureVersionID zero.Int
var featureVersionVersion zero.String
var featureVersionFixedInVersions zero.String
var featureVersionFeatureName zero.String
err := rows.Scan(
&featureVersionVersion,
&featureVersionFixedInVersions,
&featureVersionID,
&featureVersionFeatureName,
)
@ -163,7 +163,7 @@ func scanVulnerability(queryer Queryer, queryName string, vulnerabilityRow *sql.
Namespace: vulnerability.Namespace,
Name: featureVersionFeatureName.String,
},
Version: types.NewVersionUnsafe(featureVersionVersion.String),
FixedInVersions: types.NewFixedInVersionsUnsafe(featureVersionFixedInVersions.String),
}
vulnerability.FixedIn = append(vulnerability.FixedIn, featureVersion)
}
@ -274,7 +274,7 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
// for diffing existing vulnerabilities.
var fixedIn []database.FeatureVersion
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)
}
}
@ -350,7 +350,7 @@ func applyFixedInDiff(currentList, diff []database.FeatureVersion) ([]database.F
different := false
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,
// in which case we would be in the "inBothNames".
continue
@ -363,7 +363,7 @@ func applyFixedInDiff(currentList, diff []database.FeatureVersion) ([]database.F
for _, name := range inBothNames {
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.
delete(currentMap, name)
different = true
@ -438,7 +438,7 @@ func (pgSQL *pgSQL) insertVulnerabilityFixedInFeatureVersions(tx *sql.Tx, vulner
err = tx.QueryRow(
insertVulnerabilityFixedInFeature,
vulnerabilityID, fv.Feature.ID,
&fv.Version,
&fv.FixedInVersions,
).Scan(&fixedInID)
if err != nil {
@ -446,7 +446,7 @@ func (pgSQL *pgSQL) insertVulnerabilityFixedInFeatureVersions(tx *sql.Tx, vulner
}
// 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 {
return err
}
@ -455,7 +455,7 @@ func (pgSQL *pgSQL) insertVulnerabilityFixedInFeatureVersions(tx *sql.Tx, vulner
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.
// TODO(Quentin-M): LIMIT
rows, err := tx.Query(searchFeatureVersionByFeature, featureID)
@ -472,10 +472,7 @@ func linkVulnerabilityToFeatureVersions(tx *sql.Tx, fixedInID, vulnerabilityID,
if err != nil {
return handleError("searchFeatureVersionByFeature.Scan()", err)
}
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.
if fixedInVersions.Affected(affected.Version) {
affecteds = append(affecteds, affected)
}
}
@ -527,7 +524,7 @@ func (pgSQL *pgSQL) DeleteVulnerabilityFix(vulnerabilityNamespace, vulnerability
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"},
FixedIn: []database.FeatureVersion{
{
Feature: database.Feature{Name: "openssl"},
Version: types.NewVersionUnsafe("2.0"),
Feature: database.Feature{Name: "openssl"},
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 2.0"),
},
{
Feature: database.Feature{Name: "libssl"},
Version: types.NewVersionUnsafe("1.9-abc"),
Feature: database.Feature{Name: "libssl"},
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.9-abc"),
},
},
}
@ -114,50 +114,50 @@ func TestInsertVulnerability(t *testing.T) {
Name: "TestInsertVulnerabilityFeatureVersion1",
Namespace: n1,
},
Version: types.NewVersionUnsafe("1.0"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.0"),
}
f2 := database.FeatureVersion{
Feature: database.Feature{
Name: "TestInsertVulnerabilityFeatureVersion1",
Namespace: n2,
},
Version: types.NewVersionUnsafe("1.0"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.0"),
}
f3 := database.FeatureVersion{
Feature: database.Feature{
Name: "TestInsertVulnerabilityFeatureVersion2",
},
Version: types.MaxVersion,
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion),
}
f4 := database.FeatureVersion{
Feature: database.Feature{
Name: "TestInsertVulnerabilityFeatureVersion2",
},
Version: types.NewVersionUnsafe("1.4"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.4"),
}
f5 := database.FeatureVersion{
Feature: database.Feature{
Name: "TestInsertVulnerabilityFeatureVersion3",
},
Version: types.NewVersionUnsafe("1.5"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 1.5"),
}
f6 := database.FeatureVersion{
Feature: database.Feature{
Name: "TestInsertVulnerabilityFeatureVersion4",
},
Version: types.NewVersionUnsafe("0.1"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.1"),
}
f7 := database.FeatureVersion{
Feature: database.Feature{
Name: "TestInsertVulnerabilityFeatureVersion5",
},
Version: types.MaxVersion,
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion),
}
f8 := database.FeatureVersion{
Feature: database.Feature{
Name: "TestInsertVulnerabilityFeatureVersion5",
},
Version: types.MinVersion,
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MinVersion),
}
// Insert invalid vulnerabilities.

@ -196,7 +196,7 @@ func parseDebianJSON(data *jsonData) (vulnerabilities []database.Vulnerability,
Name: "debian:" + database.DebianReleasesMapping[releaseName],
},
},
Version: version,
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, version),
}
vulnerability.FixedIn = append(vulnerability.FixedIn, pkg)

@ -45,7 +45,7 @@ func TestDebianParser(t *testing.T) {
Namespace: database.Namespace{Name: "debian:8"},
Name: "aptdaemon",
},
Version: types.MaxVersion,
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion),
},
{
Feature: database.Feature{
@ -53,7 +53,7 @@ func TestDebianParser(t *testing.T) {
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"},
Name: "aptdaemon",
},
Version: types.NewVersionUnsafe("0.7.0"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.7.0"),
},
{
Feature: database.Feature{
Namespace: database.Namespace{Name: "debian:unstable"},
Name: "aptdaemon",
},
Version: types.NewVersionUnsafe("0.7.0"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.7.0"),
},
{
Feature: database.Feature{
Namespace: database.Namespace{Name: "debian:8"},
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"},
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="
updaterFlag = "nodejsUpdater"
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")
@ -131,8 +129,10 @@ func parseNodejsAdvisories(advisories []nodejsAdvisory, latestUpdate string) (vu
},
},
}
if version, err := getAdvisoryVersion(advisory.PatchedVersions); err == nil {
pkg.Version = version
if fivs, err := types.NewFixedInVersions(advisory.PatchedVersions); err == nil {
pkg.FixedInVersions = fivs
} else {
log.Warningf("could not parse nodejs patched version: '%s'.", err)
}
vulnerability.FixedIn = append(vulnerability.FixedIn, pkg)
@ -149,34 +149,5 @@ func parseNodejsAdvisories(advisories []nodejsAdvisory, latestUpdate string) (vu
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.
func (fetcher *NodejsFetcher) Clean() {}

@ -50,14 +50,14 @@ func TestNodejsParser(t *testing.T) {
Namespace: database.Namespace{Name: "nodejs:" + defaultNodejsVersion},
Name: "ldapauth",
},
Version: types.NewVersionUnsafe("2.2.4" + defaultVersionSuffix),
FixedInVersions: types.NewFixedInVersionsUnsafe("> 2.2.4"),
},
{
Feature: database.Feature{
Namespace: database.Namespace{Name: "nodejs:" + defaultNodejsVersion},
Name: "ldapauth-fork",
},
Version: types.NewVersionUnsafe("2.3.3"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 2.3.3"),
},
}
for _, expectedFeatureVersion := range expectedFeatureVersions {
@ -73,7 +73,7 @@ func TestNodejsParser(t *testing.T) {
Namespace: database.Namespace{Name: "nodejs:" + defaultNodejsVersion},
Name: "hawk",
},
Version: types.NewVersionUnsafe("4.1.1"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">=3.1.3 < 4.0.0 || >=4.1.1"),
},
}
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 ") {
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.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 {
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
}
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
} else {
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"},
Name: "xerces-c",
},
Version: types.NewVersionUnsafe("3.1.1-7.el7_1"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 3.1.1-7.el7_1"),
},
{
Feature: database.Feature{
Namespace: database.Namespace{Name: "centos:7"},
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{
Namespace: database.Namespace{Name: "centos:7"},
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"},
Name: "firefox",
},
Version: types.NewVersionUnsafe("38.1.0-1.el6_6"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 38.1.0-1.el6_6"),
},
{
Feature: database.Feature{
Namespace: database.Namespace{Name: "centos:7"},
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"]]},
Name: md["package"],
},
Version: version,
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, version),
}
vulnerability.FixedIn = append(vulnerability.FixedIn, featureVersion)
}

@ -48,21 +48,21 @@ func TestUbuntuParser(t *testing.T) {
Namespace: database.Namespace{Name: "ubuntu:14.04"},
Name: "libmspack",
},
Version: types.MaxVersion,
FixedInVersions: types.NewFixedInVersionsFromOV(types.OpGreaterEqual, types.MaxVersion),
},
{
Feature: database.Feature{
Namespace: database.Namespace{Name: "ubuntu:15.04"},
Name: "libmspack",
},
Version: types.NewVersionUnsafe("0.4-3"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.4-3"),
},
{
Feature: database.Feature{
Namespace: database.Namespace{Name: "ubuntu:15.10"},
Name: "libmspack-anotherpkg",
},
Version: types.NewVersionUnsafe("0.1"),
FixedInVersions: types.NewFixedInVersionsUnsafe(">= 0.1"),
},
}

@ -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
}

@ -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…
Cancel
Save