clair: remove types
package
This removes the `types` package instead moving the contents to the top-level clair package. This change also renames the `Priority` type to `Severity` in order to reduce confusion. This change also removes the IsValid method and replaces it with a safe constructor to avoid the creation of invalid values. Many docstrings were tweaked in the making of this commit.
This commit is contained in:
parent
02e2c58236
commit
343e24eb7e
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 clair authors
|
// Copyright 2017 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.
|
||||||
@ -24,9 +24,9 @@ import (
|
|||||||
"github.com/coreos/pkg/capnslog"
|
"github.com/coreos/pkg/capnslog"
|
||||||
"github.com/fernet/fernet-go"
|
"github.com/fernet/fernet-go"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt"
|
"github.com/coreos/clair/ext/versionfmt"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var log = capnslog.NewPackageLogger("github.com/coreos/clair", "v1")
|
var log = capnslog.NewPackageLogger("github.com/coreos/clair", "v1")
|
||||||
@ -109,9 +109,9 @@ type Vulnerability struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) {
|
func (v Vulnerability) DatabaseModel() (database.Vulnerability, error) {
|
||||||
severity := types.Priority(v.Severity)
|
severity, err := clair.NewSeverity(v.Severity)
|
||||||
if !severity.IsValid() {
|
if err != nil {
|
||||||
return database.Vulnerability{}, errors.New("Invalid severity")
|
return database.Vulnerability{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
var dbFeatures []database.FeatureVersion
|
var dbFeatures []database.FeatureVersion
|
||||||
|
75
clair.go
75
clair.go
@ -1,75 +0,0 @@
|
|||||||
// Copyright 2015 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 clair implements the ability to boot Clair with your own imports
|
|
||||||
// that can dynamically register additional functionality.
|
|
||||||
package clair
|
|
||||||
|
|
||||||
import (
|
|
||||||
"math/rand"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"syscall"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/coreos/clair/api"
|
|
||||||
"github.com/coreos/clair/api/context"
|
|
||||||
"github.com/coreos/clair/config"
|
|
||||||
"github.com/coreos/clair/database"
|
|
||||||
"github.com/coreos/clair/notifier"
|
|
||||||
"github.com/coreos/clair/updater"
|
|
||||||
"github.com/coreos/clair/utils"
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
|
||||||
)
|
|
||||||
|
|
||||||
var log = capnslog.NewPackageLogger("github.com/coreos/clair", "main")
|
|
||||||
|
|
||||||
// Boot starts Clair. By exporting this function, anyone can import their own
|
|
||||||
// custom fetchers/updaters into their own package and then call clair.Boot.
|
|
||||||
func Boot(config *config.Config) {
|
|
||||||
rand.Seed(time.Now().UnixNano())
|
|
||||||
st := utils.NewStopper()
|
|
||||||
|
|
||||||
// Open database
|
|
||||||
db, err := database.Open(config.Database)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
defer db.Close()
|
|
||||||
|
|
||||||
// Start notifier
|
|
||||||
st.Begin()
|
|
||||||
go notifier.Run(config.Notifier, db, st)
|
|
||||||
|
|
||||||
// Start API
|
|
||||||
st.Begin()
|
|
||||||
go api.Run(config.API, &context.RouteContext{db, config.API}, st)
|
|
||||||
st.Begin()
|
|
||||||
go api.RunHealth(config.API, &context.RouteContext{db, config.API}, st)
|
|
||||||
|
|
||||||
// Start updater
|
|
||||||
st.Begin()
|
|
||||||
go updater.Run(config.Updater, db, st)
|
|
||||||
|
|
||||||
// Wait for interruption and shutdown gracefully.
|
|
||||||
waitForSignals(syscall.SIGINT, syscall.SIGTERM)
|
|
||||||
log.Info("Received interruption, gracefully stopping ...")
|
|
||||||
st.Stop()
|
|
||||||
}
|
|
||||||
|
|
||||||
func waitForSignals(signals ...os.Signal) {
|
|
||||||
interrupts := make(chan os.Signal, 1)
|
|
||||||
signal.Notify(interrupts, signals...)
|
|
||||||
<-interrupts
|
|
||||||
}
|
|
@ -16,14 +16,23 @@ package main
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"flag"
|
"flag"
|
||||||
|
"math/rand"
|
||||||
"os"
|
"os"
|
||||||
|
"os/signal"
|
||||||
"runtime/pprof"
|
"runtime/pprof"
|
||||||
"strings"
|
"strings"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
|
||||||
"github.com/coreos/clair"
|
"github.com/coreos/clair/api"
|
||||||
|
"github.com/coreos/clair/api/context"
|
||||||
"github.com/coreos/clair/config"
|
"github.com/coreos/clair/config"
|
||||||
|
"github.com/coreos/clair/database"
|
||||||
|
"github.com/coreos/clair/notifier"
|
||||||
|
"github.com/coreos/clair/updater"
|
||||||
|
"github.com/coreos/clair/utils"
|
||||||
|
|
||||||
// Register database driver.
|
// Register database driver.
|
||||||
_ "github.com/coreos/clair/database/pgsql"
|
_ "github.com/coreos/clair/database/pgsql"
|
||||||
@ -50,30 +59,10 @@ import (
|
|||||||
|
|
||||||
var log = capnslog.NewPackageLogger("github.com/coreos/clair/cmd/clair", "main")
|
var log = capnslog.NewPackageLogger("github.com/coreos/clair/cmd/clair", "main")
|
||||||
|
|
||||||
func main() {
|
func waitForSignals(signals ...os.Signal) {
|
||||||
// Parse command-line arguments
|
interrupts := make(chan os.Signal, 1)
|
||||||
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
signal.Notify(interrupts, signals...)
|
||||||
flagConfigPath := flag.String("config", "/etc/clair/config.yaml", "Load configuration from the specified file.")
|
<-interrupts
|
||||||
flagCPUProfilePath := flag.String("cpu-profile", "", "Write a CPU profile to the specified file before exiting.")
|
|
||||||
flagLogLevel := flag.String("log-level", "info", "Define the logging level.")
|
|
||||||
flag.Parse()
|
|
||||||
// Load configuration
|
|
||||||
config, err := config.Load(*flagConfigPath)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatalf("failed to load configuration: %s", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initialize logging system
|
|
||||||
logLevel, err := capnslog.ParseLevel(strings.ToUpper(*flagLogLevel))
|
|
||||||
capnslog.SetGlobalLogLevel(logLevel)
|
|
||||||
capnslog.SetFormatter(capnslog.NewPrettyFormatter(os.Stdout, false))
|
|
||||||
|
|
||||||
// Enable CPU Profiling if specified
|
|
||||||
if *flagCPUProfilePath != "" {
|
|
||||||
defer stopCPUProfiling(startCPUProfiling(*flagCPUProfilePath))
|
|
||||||
}
|
|
||||||
|
|
||||||
clair.Boot(config)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCPUProfiling(path string) *os.File {
|
func startCPUProfiling(path string) *os.File {
|
||||||
@ -97,3 +86,62 @@ func stopCPUProfiling(f *os.File) {
|
|||||||
f.Close()
|
f.Close()
|
||||||
log.Info("stopped CPU profiling")
|
log.Info("stopped CPU profiling")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Boot starts Clair instance with the provided config.
|
||||||
|
func Boot(config *config.Config) {
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
st := utils.NewStopper()
|
||||||
|
|
||||||
|
// Open database
|
||||||
|
db, err := database.Open(config.Database)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
defer db.Close()
|
||||||
|
|
||||||
|
// Start notifier
|
||||||
|
st.Begin()
|
||||||
|
go notifier.Run(config.Notifier, db, st)
|
||||||
|
|
||||||
|
// Start API
|
||||||
|
st.Begin()
|
||||||
|
go api.Run(config.API, &context.RouteContext{db, config.API}, st)
|
||||||
|
st.Begin()
|
||||||
|
go api.RunHealth(config.API, &context.RouteContext{db, config.API}, st)
|
||||||
|
|
||||||
|
// Start updater
|
||||||
|
st.Begin()
|
||||||
|
go updater.Run(config.Updater, db, st)
|
||||||
|
|
||||||
|
// Wait for interruption and shutdown gracefully.
|
||||||
|
waitForSignals(syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
log.Info("Received interruption, gracefully stopping ...")
|
||||||
|
st.Stop()
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Parse command-line arguments
|
||||||
|
flag.CommandLine = flag.NewFlagSet(os.Args[0], flag.ExitOnError)
|
||||||
|
flagConfigPath := flag.String("config", "/etc/clair/config.yaml", "Load configuration from the specified file.")
|
||||||
|
flagCPUProfilePath := flag.String("cpu-profile", "", "Write a CPU profile to the specified file before exiting.")
|
||||||
|
flagLogLevel := flag.String("log-level", "info", "Define the logging level.")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
// Load configuration
|
||||||
|
config, err := config.Load(*flagConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalf("failed to load configuration: %s", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize logging system
|
||||||
|
logLevel, err := capnslog.ParseLevel(strings.ToUpper(*flagLogLevel))
|
||||||
|
capnslog.SetGlobalLogLevel(logLevel)
|
||||||
|
capnslog.SetFormatter(capnslog.NewPrettyFormatter(os.Stdout, false))
|
||||||
|
|
||||||
|
// Enable CPU Profiling if specified
|
||||||
|
if *flagCPUProfilePath != "" {
|
||||||
|
defer stopCPUProfiling(startCPUProfiling(*flagCPUProfilePath))
|
||||||
|
}
|
||||||
|
|
||||||
|
Boot(config)
|
||||||
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 clair authors
|
// Copyright 2017 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.
|
||||||
@ -29,7 +29,8 @@ var (
|
|||||||
ErrBackendException = errors.New("database: an error occured when querying the backend")
|
ErrBackendException = errors.New("database: an error occured when querying the backend")
|
||||||
|
|
||||||
// ErrInconsistent is an error that occurs when a database consistency check
|
// ErrInconsistent is an error that occurs when a database consistency check
|
||||||
// fails (ie. when an entity which is supposed to be unique is detected twice)
|
// fails (i.e. when an entity which is supposed to be unique is detected
|
||||||
|
// twice)
|
||||||
ErrInconsistent = errors.New("database: inconsistent database")
|
ErrInconsistent = errors.New("database: inconsistent database")
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -62,123 +63,151 @@ func Open(cfg config.RegistrableComponentConfig) (Datastore, error) {
|
|||||||
return driver(cfg)
|
return driver(cfg)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Datastore is the interface that describes a database backend implementation.
|
// Datastore represents the required operations on a persistent data store for
|
||||||
|
// a Clair deployment.
|
||||||
type Datastore interface {
|
type Datastore interface {
|
||||||
// # Namespace
|
|
||||||
// ListNamespaces returns the entire list of known Namespaces.
|
// ListNamespaces returns the entire list of known Namespaces.
|
||||||
ListNamespaces() ([]Namespace, error)
|
ListNamespaces() ([]Namespace, error)
|
||||||
|
|
||||||
// # Layer
|
|
||||||
// InsertLayer stores a Layer in the database.
|
// InsertLayer stores a Layer in the database.
|
||||||
// A Layer is uniquely identified by its Name. The Name and EngineVersion fields are mandatory.
|
//
|
||||||
// If a Parent is specified, it is expected that it has been retrieved using FindLayer.
|
// A Layer is uniquely identified by its Name.
|
||||||
// If a Layer that already exists is inserted and the EngineVersion of the given Layer is higher
|
// The Name and EngineVersion fields are mandatory.
|
||||||
// than the stored one, the stored Layer should be updated.
|
// If a Parent is specified, it is expected that it has been retrieved using
|
||||||
// The function has to be idempotent, inserting a layer that already exists shouln'd return an
|
// FindLayer.
|
||||||
// error.
|
// If a Layer that already exists is inserted and the EngineVersion of the
|
||||||
|
// given Layer is higher than the stored one, the stored Layer should be
|
||||||
|
// updated.
|
||||||
|
// The function has to be idempotent, inserting a layer that already exists
|
||||||
|
// shouldn't return an error.
|
||||||
InsertLayer(Layer) error
|
InsertLayer(Layer) error
|
||||||
|
|
||||||
// FindLayer retrieves a Layer from the database.
|
// FindLayer retrieves a Layer from the database.
|
||||||
// withFeatures specifies whether the Features field should be filled. When withVulnerabilities is
|
//
|
||||||
// true, the Features field should be filled and their AffectedBy fields should contain every
|
// When `withFeatures` is true, the Features field should be filled.
|
||||||
// vulnerabilities that affect them.
|
// When `withVulnerabilities` is true, the Features field should be filled
|
||||||
|
// and their AffectedBy fields should contain every vulnerabilities that
|
||||||
|
// affect them.
|
||||||
FindLayer(name string, withFeatures, withVulnerabilities bool) (Layer, error)
|
FindLayer(name string, withFeatures, withVulnerabilities bool) (Layer, error)
|
||||||
|
|
||||||
// DeleteLayer deletes a Layer from the database and every layers that are based on it,
|
// DeleteLayer deletes a Layer from the database and every layers that are
|
||||||
// recursively.
|
// based on it, recursively.
|
||||||
DeleteLayer(name string) error
|
DeleteLayer(name string) error
|
||||||
|
|
||||||
// # Vulnerability
|
// ListVulnerabilities returns the list of vulnerabilities of a particular
|
||||||
// ListVulnerabilities returns the list of vulnerabilies of a certain Namespace.
|
// Namespace.
|
||||||
|
//
|
||||||
// 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.
|
||||||
// If there is no more page, -1 has to be returned.
|
// The function should return the next available page. If there are no more
|
||||||
|
// pages, -1 has to be returned.
|
||||||
ListVulnerabilities(namespaceName string, limit int, page int) ([]Vulnerability, int, error)
|
ListVulnerabilities(namespaceName string, 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,
|
||||||
// necessary. A vulnerability is uniquely identified by its Namespace and its Name.
|
// updating them if necessary.
|
||||||
// The FixedIn field may only contain a partial list of Features that are affected by the
|
//
|
||||||
// Vulnerability, along with the version in which the vulnerability is fixed. It is the
|
// A vulnerability is uniquely identified by its Namespace and its Name.
|
||||||
// responsibility of the implementation to update the list properly. A version equals to
|
// The FixedIn field may only contain a partial list of Features that are
|
||||||
// types.MinVersion means that the given Feature is not being affected by the Vulnerability at
|
// affected by the Vulnerability, along with the version in which the
|
||||||
// all and thus, should be removed from the list. It is important that Features should be unique
|
// vulnerability is fixed. It is the responsibility of the implementation to
|
||||||
// in the FixedIn list. For example, it doesn't make sense to have two `openssl` Feature listed as
|
// update the list properly.
|
||||||
// a Vulnerability can only be fixed in one Version. This is true because Vulnerabilities and
|
// A version equals to versionfmt.MinVersion means that the given Feature is
|
||||||
// Features are Namespaced (i.e. specific to one operating system).
|
// not being affected by the Vulnerability at all and thus, should be removed
|
||||||
// Each vulnerability insertion or update has to create a Notification that will contain the
|
// from the list.
|
||||||
// old and the updated Vulnerability, unless createNotification equals to true.
|
// It is important that Features should be unique in the FixedIn list. For
|
||||||
|
// example, it doesn't make sense to have two `openssl` Feature listed as a
|
||||||
|
// Vulnerability can only be fixed in one Version. This is true because
|
||||||
|
// Vulnerabilities and Features are namespaced (i.e. specific to one
|
||||||
|
// operating system).
|
||||||
|
// Each vulnerability insertion or update has to create a Notification that
|
||||||
|
// will contain the old and the updated Vulnerability, unless
|
||||||
|
// createNotification equals to true.
|
||||||
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(namespaceName, name string) (Vulnerability, error)
|
FindVulnerability(namespaceName, 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(namespaceName, name string) error
|
DeleteVulnerability(namespaceName, 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
|
||||||
// the specified Vulnerability in the database.
|
// of existing ones to 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, vulnerabilityName string, fixes []FeatureVersion) error
|
InsertVulnerabilityFixes(vulnerabilityNamespace, vulnerabilityName string, fixes []FeatureVersion) error
|
||||||
|
|
||||||
// DeleteVulnerabilityFix removes a FixedIn Feature from the specified Vulnerability in the
|
// DeleteVulnerabilityFix removes a FixedIn Feature from the specified
|
||||||
// database. It can be used to store the fact that a Vulnerability no longer affects the given
|
// Vulnerability in the database. It can be used to store the fact that a
|
||||||
// Feature in any Version.
|
// Vulnerability no longer affects the given 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, vulnerabilityName, featureName string) error
|
DeleteVulnerabilityFix(vulnerabilityNamespace, vulnerabilityName, featureName string) error
|
||||||
|
|
||||||
// # Notification
|
// GetAvailableNotification returns the Name, Created, Notified and Deleted
|
||||||
// GetAvailableNotification returns the Name, Created, Notified and Deleted fields of a
|
// fields of a Notification that should be handled.
|
||||||
// Notification that should be handled. The renotify interval defines how much time after being
|
//
|
||||||
// marked as Notified by SetNotificationNotified, a Notification that hasn't been deleted should
|
// The renotify interval defines how much time after being marked as Notified
|
||||||
// be returned again by this function. A Notification for which there is a valid Lock with the
|
// by SetNotificationNotified, a Notification that hasn't been deleted should
|
||||||
// same Name should not be returned.
|
// be returned again by this function.
|
||||||
|
// A Notification for which there is a valid Lock with the same Name should
|
||||||
|
// not be returned.
|
||||||
GetAvailableNotification(renotifyInterval time.Duration) (VulnerabilityNotification, error)
|
GetAvailableNotification(renotifyInterval time.Duration) (VulnerabilityNotification, error)
|
||||||
|
|
||||||
// GetNotification returns a Notification, including its OldVulnerability and NewVulnerability
|
// GetNotification returns a Notification, including its OldVulnerability and
|
||||||
// fields. On these Vulnerabilities, LayersIntroducingVulnerability should be filled with
|
// NewVulnerability fields.
|
||||||
// every Layer that introduces the Vulnerability (i.e. adds at least one affected FeatureVersion).
|
//
|
||||||
// The Limit and page parameters are used to paginate LayersIntroducingVulnerability. The first
|
// On these Vulnerabilities, LayersIntroducingVulnerability should be filled
|
||||||
// given page should be VulnerabilityNotificationFirstPage. The function will then return the next
|
// with every Layer that introduces the Vulnerability (i.e. adds at least one
|
||||||
// availage page. If there is no more page, NoVulnerabilityNotificationPage has to be returned.
|
// affected FeatureVersion).
|
||||||
|
// The Limit and page parameters are used to paginate
|
||||||
|
// LayersIntroducingVulnerability. The first given page should be
|
||||||
|
// VulnerabilityNotificationFirstPage. The function will then return the next
|
||||||
|
// available page. If there is no more page, NoVulnerabilityNotificationPage
|
||||||
|
// has to be returned.
|
||||||
GetNotification(name string, limit int, page VulnerabilityNotificationPageNumber) (VulnerabilityNotification, VulnerabilityNotificationPageNumber, error)
|
GetNotification(name string, limit int, page VulnerabilityNotificationPageNumber) (VulnerabilityNotification, VulnerabilityNotificationPageNumber, error)
|
||||||
|
|
||||||
// SetNotificationNotified marks a Notification as notified and thus, makes it unavailable for
|
// SetNotificationNotified marks a Notification as notified and thus, makes
|
||||||
// GetAvailableNotification, until the renotify duration is elapsed.
|
// it unavailable for GetAvailableNotification, until the renotify duration
|
||||||
|
// is elapsed.
|
||||||
SetNotificationNotified(name string) error
|
SetNotificationNotified(name string) error
|
||||||
|
|
||||||
// DeleteNotification marks a Notification as deleted, and thus, makes it unavailable for
|
// DeleteNotification marks a Notification as deleted, and thus, makes it
|
||||||
// GetAvailableNotification.
|
// unavailable for GetAvailableNotification.
|
||||||
DeleteNotification(name string) error
|
DeleteNotification(name string) error
|
||||||
|
|
||||||
// # Key/Value
|
|
||||||
// InsertKeyValue stores or updates a simple key/value pair in the database.
|
// InsertKeyValue stores or updates a simple key/value pair in the database.
|
||||||
InsertKeyValue(key, value string) error
|
InsertKeyValue(key, value string) error
|
||||||
|
|
||||||
// GetKeyValue retrieves a value from the database from the given key.
|
// GetKeyValue retrieves a value from the database from the given key.
|
||||||
|
//
|
||||||
// It returns an empty string if there is no such key.
|
// It returns an empty string if there is no such key.
|
||||||
GetKeyValue(key string) (string, error)
|
GetKeyValue(key string) (string, error)
|
||||||
|
|
||||||
// # Lock
|
// Lock creates or renew a Lock in the database with the given name, owner
|
||||||
// Lock creates or renew a Lock in the database with the given name, owner and duration.
|
// and duration.
|
||||||
// After the specified duration, the Lock expires by itself if it hasn't been unlocked, and thus,
|
//
|
||||||
// let other users create a Lock with the same name. However, the owner can renew its Lock by
|
// After the specified duration, the Lock expires by itself if it hasn't been
|
||||||
// setting renew to true. Lock should not block, it should instead returns whether the Lock has
|
// unlocked, and thus, let other users create a Lock with the same name.
|
||||||
// been successfully acquired/renewed. If it's the case, the expiration time of that Lock is
|
// However, the owner can renew its Lock by setting renew to true.
|
||||||
// returned as well.
|
// Lock should not block, it should instead returns whether the Lock has been
|
||||||
|
// successfully acquired/renewed. If it's the case, the expiration time of
|
||||||
|
// that Lock is returned as well.
|
||||||
Lock(name string, owner string, duration time.Duration, renew bool) (bool, time.Time)
|
Lock(name string, owner string, duration time.Duration, renew bool) (bool, time.Time)
|
||||||
|
|
||||||
// Unlock releases an existing Lock.
|
// Unlock releases an existing Lock.
|
||||||
Unlock(name, owner string)
|
Unlock(name, owner string)
|
||||||
|
|
||||||
// FindLock returns the owner of a Lock specified by the name, and its experation time if it
|
// FindLock returns the owner of a Lock specified by the name, and its
|
||||||
// exists.
|
// expiration time if it exists.
|
||||||
FindLock(name string) (string, time.Time, error)
|
FindLock(name string) (string, time.Time, error)
|
||||||
|
|
||||||
// # Miscellaneous
|
|
||||||
// Ping returns the health status of the database.
|
// Ping returns the health status of the database.
|
||||||
Ping() bool
|
Ping() bool
|
||||||
|
|
||||||
// Close closes the database and free any allocated resource.
|
// Close closes the database and frees any allocated resource.
|
||||||
Close()
|
Close()
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 clair authors
|
// Copyright 2017 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.
|
||||||
@ -19,7 +19,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/clair/utils/types"
|
"github.com/coreos/clair"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ID is only meant to be used by database implementations and should never be used for anything else.
|
// ID is only meant to be used by database implementations and should never be used for anything else.
|
||||||
@ -70,7 +70,7 @@ type Vulnerability struct {
|
|||||||
|
|
||||||
Description string
|
Description string
|
||||||
Link string
|
Link string
|
||||||
Severity types.Priority
|
Severity clair.Severity
|
||||||
|
|
||||||
Metadata MetadataMap
|
Metadata MetadataMap
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 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.
|
||||||
@ -26,10 +26,10 @@ import (
|
|||||||
"github.com/pborman/uuid"
|
"github.com/pborman/uuid"
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/utils"
|
"github.com/coreos/clair/utils"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -93,7 +93,7 @@ func TestRaceAffects(t *testing.T) {
|
|||||||
Version: strconv.Itoa(version),
|
Version: strconv.Itoa(version),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
Severity: types.Unknown,
|
Severity: clair.Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
vulnerabilities[version] = append(vulnerabilities[version], vulnerability)
|
vulnerabilities[version] = append(vulnerabilities[version], vulnerability)
|
||||||
|
@ -20,10 +20,10 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFindLayer(t *testing.T) {
|
func TestFindLayer(t *testing.T) {
|
||||||
@ -91,7 +91,7 @@ func TestFindLayer(t *testing.T) {
|
|||||||
if assert.Len(t, featureVersion.AffectedBy, 1) {
|
if assert.Len(t, featureVersion.AffectedBy, 1) {
|
||||||
assert.Equal(t, "debian:7", featureVersion.AffectedBy[0].Namespace.Name)
|
assert.Equal(t, "debian:7", featureVersion.AffectedBy[0].Namespace.Name)
|
||||||
assert.Equal(t, "CVE-OPENSSL-1-DEB7", featureVersion.AffectedBy[0].Name)
|
assert.Equal(t, "CVE-OPENSSL-1-DEB7", featureVersion.AffectedBy[0].Name)
|
||||||
assert.Equal(t, types.High, featureVersion.AffectedBy[0].Severity)
|
assert.Equal(t, clair.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, "2.0", featureVersion.AffectedBy[0].FixedBy)
|
assert.Equal(t, "2.0", featureVersion.AffectedBy[0].FixedBy)
|
||||||
|
@ -20,11 +20,11 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt"
|
"github.com/coreos/clair/ext/versionfmt"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestNotification(t *testing.T) {
|
func TestNotification(t *testing.T) {
|
||||||
@ -169,7 +169,7 @@ func TestNotification(t *testing.T) {
|
|||||||
|
|
||||||
// Update a vulnerability and ensure that the old/new vulnerabilities are correct.
|
// Update a vulnerability and ensure that the old/new vulnerabilities are correct.
|
||||||
v1b := v1
|
v1b := v1
|
||||||
v1b.Severity = types.High
|
v1b.Severity = clair.High
|
||||||
v1b.FixedIn = []database.FeatureVersion{
|
v1b.FixedIn = []database.FeatureVersion{
|
||||||
{
|
{
|
||||||
Feature: f1,
|
Feature: f1,
|
||||||
|
@ -17,7 +17,6 @@ package pgsql
|
|||||||
import (
|
import (
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@ -197,11 +196,7 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
|
|||||||
if vulnerability.Name == "" || vulnerability.Namespace.Name == "" {
|
if vulnerability.Name == "" || vulnerability.Namespace.Name == "" {
|
||||||
return commonerr.NewBadRequestError("insertVulnerability needs at least the Name and the Namespace")
|
return commonerr.NewBadRequestError("insertVulnerability needs at least the Name and the Namespace")
|
||||||
}
|
}
|
||||||
if !onlyFixedIn && !vulnerability.Severity.IsValid() {
|
|
||||||
msg := fmt.Sprintf("could not insert a vulnerability that has an invalid Severity: %s", vulnerability.Severity)
|
|
||||||
log.Warning(msg)
|
|
||||||
return commonerr.NewBadRequestError(msg)
|
|
||||||
}
|
|
||||||
for i := 0; i < len(vulnerability.FixedIn); i++ {
|
for i := 0; i < len(vulnerability.FixedIn); i++ {
|
||||||
fifv := &vulnerability.FixedIn[i]
|
fifv := &vulnerability.FixedIn[i]
|
||||||
|
|
||||||
@ -271,8 +266,9 @@ func (pgSQL *pgSQL) insertVulnerability(vulnerability database.Vulnerability, on
|
|||||||
return handleError("removeVulnerability", err)
|
return handleError("removeVulnerability", err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// The vulnerability is new, we don't want to have any types.MinVersion as they are only used
|
// The vulnerability is new, we don't want to have any
|
||||||
// for diffing existing vulnerabilities.
|
// versionfmt.MinVersion as they are only used 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 != versionfmt.MinVersion {
|
if fv.Version != versionfmt.MinVersion {
|
||||||
|
@ -20,11 +20,11 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt"
|
"github.com/coreos/clair/ext/versionfmt"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestFindVulnerability(t *testing.T) {
|
func TestFindVulnerability(t *testing.T) {
|
||||||
@ -44,7 +44,7 @@ func TestFindVulnerability(t *testing.T) {
|
|||||||
Name: "CVE-OPENSSL-1-DEB7",
|
Name: "CVE-OPENSSL-1-DEB7",
|
||||||
Description: "A vulnerability affecting OpenSSL < 2.0 on Debian 7.0",
|
Description: "A vulnerability affecting OpenSSL < 2.0 on Debian 7.0",
|
||||||
Link: "http://google.com/#q=CVE-OPENSSL-1-DEB7",
|
Link: "http://google.com/#q=CVE-OPENSSL-1-DEB7",
|
||||||
Severity: types.High,
|
Severity: clair.High,
|
||||||
Namespace: database.Namespace{
|
Namespace: database.Namespace{
|
||||||
Name: "debian:7",
|
Name: "debian:7",
|
||||||
VersionFormat: dpkg.ParserName,
|
VersionFormat: dpkg.ParserName,
|
||||||
@ -74,7 +74,7 @@ func TestFindVulnerability(t *testing.T) {
|
|||||||
Name: "debian:7",
|
Name: "debian:7",
|
||||||
VersionFormat: dpkg.ParserName,
|
VersionFormat: dpkg.ParserName,
|
||||||
},
|
},
|
||||||
Severity: types.Unknown,
|
Severity: clair.Unknown,
|
||||||
}
|
}
|
||||||
|
|
||||||
v2f, err := datastore.FindVulnerability("debian:7", "CVE-NOPE")
|
v2f, err := datastore.FindVulnerability("debian:7", "CVE-NOPE")
|
||||||
@ -180,30 +180,24 @@ func TestInsertVulnerability(t *testing.T) {
|
|||||||
Name: "",
|
Name: "",
|
||||||
Namespace: n1,
|
Namespace: n1,
|
||||||
FixedIn: []database.FeatureVersion{f1},
|
FixedIn: []database.FeatureVersion{f1},
|
||||||
Severity: types.Unknown,
|
Severity: clair.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "TestInsertVulnerability0",
|
Name: "TestInsertVulnerability0",
|
||||||
Namespace: database.Namespace{},
|
Namespace: database.Namespace{},
|
||||||
FixedIn: []database.FeatureVersion{f1},
|
FixedIn: []database.FeatureVersion{f1},
|
||||||
Severity: types.Unknown,
|
Severity: clair.Unknown,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Name: "TestInsertVulnerability0-",
|
Name: "TestInsertVulnerability0-",
|
||||||
Namespace: database.Namespace{},
|
Namespace: database.Namespace{},
|
||||||
FixedIn: []database.FeatureVersion{f1},
|
FixedIn: []database.FeatureVersion{f1},
|
||||||
},
|
},
|
||||||
{
|
|
||||||
Name: "TestInsertVulnerability0",
|
|
||||||
Namespace: n1,
|
|
||||||
FixedIn: []database.FeatureVersion{f1},
|
|
||||||
Severity: types.Priority(""),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
Name: "TestInsertVulnerability0",
|
Name: "TestInsertVulnerability0",
|
||||||
Namespace: n1,
|
Namespace: n1,
|
||||||
FixedIn: []database.FeatureVersion{f2},
|
FixedIn: []database.FeatureVersion{f2},
|
||||||
Severity: types.Unknown,
|
Severity: clair.Unknown,
|
||||||
},
|
},
|
||||||
} {
|
} {
|
||||||
err := datastore.InsertVulnerabilities([]database.Vulnerability{vulnerability}, true)
|
err := datastore.InsertVulnerabilities([]database.Vulnerability{vulnerability}, true)
|
||||||
@ -223,7 +217,7 @@ func TestInsertVulnerability(t *testing.T) {
|
|||||||
Name: "TestInsertVulnerability1",
|
Name: "TestInsertVulnerability1",
|
||||||
Namespace: n1,
|
Namespace: n1,
|
||||||
FixedIn: []database.FeatureVersion{f1, f3, f6, f7},
|
FixedIn: []database.FeatureVersion{f1, f3, f6, f7},
|
||||||
Severity: types.Low,
|
Severity: clair.Low,
|
||||||
Description: "TestInsertVulnerabilityDescription1",
|
Description: "TestInsertVulnerabilityDescription1",
|
||||||
Link: "TestInsertVulnerabilityLink1",
|
Link: "TestInsertVulnerabilityLink1",
|
||||||
Metadata: v1meta,
|
Metadata: v1meta,
|
||||||
@ -239,7 +233,7 @@ func TestInsertVulnerability(t *testing.T) {
|
|||||||
// Update vulnerability.
|
// Update vulnerability.
|
||||||
v1.Description = "TestInsertVulnerabilityLink2"
|
v1.Description = "TestInsertVulnerabilityLink2"
|
||||||
v1.Link = "TestInsertVulnerabilityLink2"
|
v1.Link = "TestInsertVulnerabilityLink2"
|
||||||
v1.Severity = types.High
|
v1.Severity = clair.High
|
||||||
// Update f3 in f4, add fixed in f5, add fixed in f6 which already exists,
|
// Update f3 in f4, add fixed in f5, add fixed in f6 which already exists,
|
||||||
// removes fixed in f7 by adding f8 which is f7 but with MinVersion, and
|
// removes fixed in f7 by adding f8 which is f7 but with MinVersion, and
|
||||||
// add fixed by f5 a second time (duplicated).
|
// add fixed by f5 a second time (duplicated).
|
||||||
|
@ -19,8 +19,8 @@ package vulnmdsrc
|
|||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
@ -29,7 +29,7 @@ var (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// AppendFunc is the type of a callback provided to an Appender.
|
// AppendFunc is the type of a callback provided to an Appender.
|
||||||
type AppendFunc func(metadataKey string, metadata interface{}, severity types.Priority)
|
type AppendFunc func(metadataKey string, metadata interface{}, severity clair.Severity)
|
||||||
|
|
||||||
// Appender represents anything that can fetch vulnerability metadata and
|
// Appender represents anything that can fetch vulnerability metadata and
|
||||||
// append it to a Vulnerability.
|
// append it to a Vulnerability.
|
||||||
|
@ -28,15 +28,14 @@ import (
|
|||||||
"os"
|
"os"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/vulnmdsrc"
|
"github.com/coreos/clair/ext/vulnmdsrc"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -52,7 +51,6 @@ type appender struct {
|
|||||||
localPath string
|
localPath string
|
||||||
dataFeedHashes map[string]string
|
dataFeedHashes map[string]string
|
||||||
metadata map[string]NVDMetadata
|
metadata map[string]NVDMetadata
|
||||||
sync.Mutex
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type NVDMetadata struct {
|
type NVDMetadata struct {
|
||||||
@ -69,9 +67,6 @@ func init() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *appender) BuildCache(datastore database.Datastore) error {
|
func (a *appender) BuildCache(datastore database.Datastore) error {
|
||||||
a.Lock()
|
|
||||||
defer a.Unlock()
|
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
a.metadata = make(map[string]NVDMetadata)
|
a.metadata = make(map[string]NVDMetadata)
|
||||||
|
|
||||||
@ -115,27 +110,18 @@ func (a *appender) BuildCache(datastore database.Datastore) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *appender) Append(vulnName string, appendFunc vulnmdsrc.AppendFunc) error {
|
func (a *appender) Append(vulnName string, appendFunc vulnmdsrc.AppendFunc) error {
|
||||||
a.Lock()
|
|
||||||
defer a.Unlock()
|
|
||||||
|
|
||||||
if nvdMetadata, ok := a.metadata[vulnName]; ok {
|
if nvdMetadata, ok := a.metadata[vulnName]; ok {
|
||||||
appendFunc(appenderName, nvdMetadata, scoreToPriority(nvdMetadata.CVSSv2.Score))
|
appendFunc(appenderName, nvdMetadata, SeverityFromCVSS(nvdMetadata.CVSSv2.Score))
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *appender) PurgeCache() {
|
func (a *appender) PurgeCache() {
|
||||||
a.Lock()
|
|
||||||
defer a.Unlock()
|
|
||||||
|
|
||||||
a.metadata = nil
|
a.metadata = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *appender) Clean() {
|
func (a *appender) Clean() {
|
||||||
a.Lock()
|
|
||||||
defer a.Unlock()
|
|
||||||
|
|
||||||
os.RemoveAll(a.localPath)
|
os.RemoveAll(a.localPath)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -232,23 +218,23 @@ func getHashFromMetaURL(metaURL string) (string, error) {
|
|||||||
return "", errors.New("invalid .meta file format")
|
return "", errors.New("invalid .meta file format")
|
||||||
}
|
}
|
||||||
|
|
||||||
// scoreToPriority converts the CVSS Score (0.0 - 10.0) into user-friendy
|
// SeverityFromCVSS converts the CVSS Score (0.0 - 10.0) into a clair.Severity
|
||||||
// types.Priority following the qualitative rating scale available in the
|
// following the qualitative rating scale available in the CVSS v3.0
|
||||||
// CVSS v3.0 specification (https://www.first.org/cvss/specification-document),
|
// specification (https://www.first.org/cvss/specification-document), Table 14.
|
||||||
// Table 14. The Negligible level is set for CVSS scores between [0, 1),
|
// The Negligible level is set for CVSS scores between [0, 1), replacing the
|
||||||
// replacing the specified None level, originally used for a score of 0.
|
// specified None level, originally used for a score of 0.
|
||||||
func scoreToPriority(score float64) types.Priority {
|
func SeverityFromCVSS(score float64) clair.Severity {
|
||||||
switch {
|
switch {
|
||||||
case score < 1.0:
|
case score < 1.0:
|
||||||
return types.Negligible
|
return clair.Negligible
|
||||||
case score < 3.9:
|
case score < 3.9:
|
||||||
return types.Low
|
return clair.Low
|
||||||
case score < 6.9:
|
case score < 6.9:
|
||||||
return types.Medium
|
return clair.Medium
|
||||||
case score < 8.9:
|
case score < 8.9:
|
||||||
return types.High
|
return clair.High
|
||||||
case score <= 10:
|
case score <= 10:
|
||||||
return types.Critical
|
return clair.Critical
|
||||||
}
|
}
|
||||||
return types.Unknown
|
return clair.Unknown
|
||||||
}
|
}
|
||||||
|
@ -27,13 +27,13 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt"
|
"github.com/coreos/clair/ext/versionfmt"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/ext/vulnsrc"
|
"github.com/coreos/clair/ext/vulnsrc"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
"github.com/coreos/clair/utils"
|
"github.com/coreos/clair/utils"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -225,7 +225,7 @@ func parse33YAML(r io.Reader) (vulns []database.Vulnerability, err error) {
|
|||||||
|
|
||||||
vulns = append(vulns, database.Vulnerability{
|
vulns = append(vulns, database.Vulnerability{
|
||||||
Name: fix,
|
Name: fix,
|
||||||
Severity: types.Unknown,
|
Severity: clair.Unknown,
|
||||||
Link: nvdURLPrefix + fix,
|
Link: nvdURLPrefix + fix,
|
||||||
FixedIn: []database.FeatureVersion{
|
FixedIn: []database.FeatureVersion{
|
||||||
{
|
{
|
||||||
@ -279,7 +279,7 @@ func parse34YAML(r io.Reader) (vulns []database.Vulnerability, err error) {
|
|||||||
|
|
||||||
for _, vulnStr := range vulnStrs {
|
for _, vulnStr := range vulnStrs {
|
||||||
var vuln database.Vulnerability
|
var vuln database.Vulnerability
|
||||||
vuln.Severity = types.Unknown
|
vuln.Severity = clair.Unknown
|
||||||
vuln.Name = vulnStr
|
vuln.Name = vulnStr
|
||||||
vuln.Link = nvdURLPrefix + vulnStr
|
vuln.Link = nvdURLPrefix + vulnStr
|
||||||
vuln.FixedIn = []database.FeatureVersion{
|
vuln.FixedIn = []database.FeatureVersion{
|
||||||
|
@ -27,12 +27,12 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt"
|
"github.com/coreos/clair/ext/versionfmt"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/ext/vulnsrc"
|
"github.com/coreos/clair/ext/vulnsrc"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -158,17 +158,17 @@ func parseDebianJSON(data *jsonData) (vulnerabilities []database.Vulnerability,
|
|||||||
vulnerability = &database.Vulnerability{
|
vulnerability = &database.Vulnerability{
|
||||||
Name: vulnName,
|
Name: vulnName,
|
||||||
Link: strings.Join([]string{cveURLPrefix, "/", vulnName}, ""),
|
Link: strings.Join([]string{cveURLPrefix, "/", vulnName}, ""),
|
||||||
Severity: types.Unknown,
|
Severity: clair.Unknown,
|
||||||
Description: vulnNode.Description,
|
Description: vulnNode.Description,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the priority of the vulnerability.
|
// Set the priority of the vulnerability.
|
||||||
// In the JSON, a vulnerability has one urgency per package it affects.
|
// In the JSON, a vulnerability has one urgency per package it affects.
|
||||||
// The highest urgency should be the one set.
|
severity := SeverityFromUrgency(releaseNode.Urgency)
|
||||||
urgency := urgencyToSeverity(releaseNode.Urgency)
|
if severity.Compare(vulnerability.Severity) > 0 {
|
||||||
if urgency.Compare(vulnerability.Severity) > 0 {
|
// The highest urgency should be the one set.
|
||||||
vulnerability.Severity = urgency
|
vulnerability.Severity = severity
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine the version of the package the vulnerability affects.
|
// Determine the version of the package the vulnerability affects.
|
||||||
@ -219,39 +219,41 @@ func parseDebianJSON(data *jsonData) (vulnerabilities []database.Vulnerability,
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func urgencyToSeverity(urgency string) types.Priority {
|
// SeverityFromUrgency converts the urgency scale used by the Debian Security
|
||||||
|
// Bug Tracker into a clair.Severity.
|
||||||
|
func SeverityFromUrgency(urgency string) clair.Severity {
|
||||||
switch urgency {
|
switch urgency {
|
||||||
case "not yet assigned":
|
case "not yet assigned":
|
||||||
return types.Unknown
|
return clair.Unknown
|
||||||
|
|
||||||
case "end-of-life":
|
case "end-of-life":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "unimportant":
|
case "unimportant":
|
||||||
return types.Negligible
|
return clair.Negligible
|
||||||
|
|
||||||
case "low":
|
case "low":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "low*":
|
case "low*":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "low**":
|
case "low**":
|
||||||
return types.Low
|
return clair.Low
|
||||||
|
|
||||||
case "medium":
|
case "medium":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "medium*":
|
case "medium*":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "medium**":
|
case "medium**":
|
||||||
return types.Medium
|
return clair.Medium
|
||||||
|
|
||||||
case "high":
|
case "high":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "high*":
|
case "high*":
|
||||||
fallthrough
|
fallthrough
|
||||||
case "high**":
|
case "high**":
|
||||||
return types.High
|
return clair.High
|
||||||
|
|
||||||
default:
|
default:
|
||||||
log.Warningf("could not determine vulnerability priority from: %s", urgency)
|
log.Warningf("could not determine vulnerability severity from: %s", urgency)
|
||||||
return types.Unknown
|
return clair.Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 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.
|
||||||
@ -20,10 +20,10 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt"
|
"github.com/coreos/clair/ext/versionfmt"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -37,7 +37,7 @@ func TestDebianParser(t *testing.T) {
|
|||||||
for _, vulnerability := range response.Vulnerabilities {
|
for _, vulnerability := range response.Vulnerabilities {
|
||||||
if vulnerability.Name == "CVE-2015-1323" {
|
if vulnerability.Name == "CVE-2015-1323" {
|
||||||
assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2015-1323", vulnerability.Link)
|
assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2015-1323", vulnerability.Link)
|
||||||
assert.Equal(t, types.Low, vulnerability.Severity)
|
assert.Equal(t, clair.Low, vulnerability.Severity)
|
||||||
assert.Equal(t, "This vulnerability is not very dangerous.", vulnerability.Description)
|
assert.Equal(t, "This vulnerability is not very dangerous.", vulnerability.Description)
|
||||||
|
|
||||||
expectedFeatureVersions := []database.FeatureVersion{
|
expectedFeatureVersions := []database.FeatureVersion{
|
||||||
@ -68,7 +68,7 @@ func TestDebianParser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
} else if vulnerability.Name == "CVE-2003-0779" {
|
} else if vulnerability.Name == "CVE-2003-0779" {
|
||||||
assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2003-0779", vulnerability.Link)
|
assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2003-0779", vulnerability.Link)
|
||||||
assert.Equal(t, types.High, vulnerability.Severity)
|
assert.Equal(t, clair.High, vulnerability.Severity)
|
||||||
assert.Equal(t, "But this one is very dangerous.", vulnerability.Description)
|
assert.Equal(t, "But this one is very dangerous.", vulnerability.Description)
|
||||||
|
|
||||||
expectedFeatureVersions := []database.FeatureVersion{
|
expectedFeatureVersions := []database.FeatureVersion{
|
||||||
@ -109,7 +109,7 @@ func TestDebianParser(t *testing.T) {
|
|||||||
}
|
}
|
||||||
} else if vulnerability.Name == "CVE-2013-2685" {
|
} else if vulnerability.Name == "CVE-2013-2685" {
|
||||||
assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2013-2685", vulnerability.Link)
|
assert.Equal(t, "https://security-tracker.debian.org/tracker/CVE-2013-2685", vulnerability.Link)
|
||||||
assert.Equal(t, types.Negligible, vulnerability.Severity)
|
assert.Equal(t, clair.Negligible, vulnerability.Severity)
|
||||||
assert.Equal(t, "Un-affected packages.", vulnerability.Description)
|
assert.Equal(t, "Un-affected packages.", vulnerability.Description)
|
||||||
|
|
||||||
expectedFeatureVersions := []database.FeatureVersion{
|
expectedFeatureVersions := []database.FeatureVersion{
|
||||||
|
@ -25,13 +25,14 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt"
|
"github.com/coreos/clair/ext/versionfmt"
|
||||||
"github.com/coreos/clair/ext/versionfmt/rpm"
|
"github.com/coreos/clair/ext/versionfmt/rpm"
|
||||||
"github.com/coreos/clair/ext/vulnsrc"
|
"github.com/coreos/clair/ext/vulnsrc"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -172,7 +173,7 @@ func parseELSA(ovalReader io.Reader) (vulnerabilities []database.Vulnerability,
|
|||||||
vulnerability := database.Vulnerability{
|
vulnerability := database.Vulnerability{
|
||||||
Name: name(definition),
|
Name: name(definition),
|
||||||
Link: link(definition),
|
Link: link(definition),
|
||||||
Severity: priority(definition),
|
Severity: severity(definition),
|
||||||
Description: description(definition),
|
Description: description(definition),
|
||||||
}
|
}
|
||||||
for _, p := range pkgs {
|
for _, p := range pkgs {
|
||||||
@ -336,24 +337,20 @@ func link(def definition) (link string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func priority(def definition) types.Priority {
|
func severity(def definition) clair.Severity {
|
||||||
// Parse the priority.
|
switch strings.ToLower(def.Severity) {
|
||||||
priority := strings.ToLower(def.Severity)
|
|
||||||
|
|
||||||
// Normalize the priority.
|
|
||||||
switch priority {
|
|
||||||
case "n/a":
|
case "n/a":
|
||||||
return types.Negligible
|
return clair.Negligible
|
||||||
case "low":
|
case "low":
|
||||||
return types.Low
|
return clair.Low
|
||||||
case "moderate":
|
case "moderate":
|
||||||
return types.Medium
|
return clair.Medium
|
||||||
case "important":
|
case "important":
|
||||||
return types.High
|
return clair.High
|
||||||
case "critical":
|
case "critical":
|
||||||
return types.Critical
|
return clair.Critical
|
||||||
default:
|
default:
|
||||||
log.Warningf("could not determine vulnerability priority from: %s.", priority)
|
log.Warningf("could not determine vulnerability severity from: %s.", def.Severity)
|
||||||
return types.Unknown
|
return clair.Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 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.
|
||||||
@ -20,9 +20,9 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt/rpm"
|
"github.com/coreos/clair/ext/versionfmt/rpm"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ func TestOracleParser(t *testing.T) {
|
|||||||
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
|
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
|
||||||
assert.Equal(t, "ELSA-2015-1193", vulnerabilities[0].Name)
|
assert.Equal(t, "ELSA-2015-1193", vulnerabilities[0].Name)
|
||||||
assert.Equal(t, "http://linux.oracle.com/errata/ELSA-2015-1193.html", vulnerabilities[0].Link)
|
assert.Equal(t, "http://linux.oracle.com/errata/ELSA-2015-1193.html", vulnerabilities[0].Link)
|
||||||
assert.Equal(t, types.Medium, vulnerabilities[0].Severity)
|
assert.Equal(t, clair.Medium, vulnerabilities[0].Severity)
|
||||||
assert.Equal(t, ` [3.1.1-7] Resolves: rhbz#1217104 CVE-2015-0252 `, vulnerabilities[0].Description)
|
assert.Equal(t, ` [3.1.1-7] Resolves: rhbz#1217104 CVE-2015-0252 `, vulnerabilities[0].Description)
|
||||||
|
|
||||||
expectedFeatureVersions := []database.FeatureVersion{
|
expectedFeatureVersions := []database.FeatureVersion{
|
||||||
@ -86,7 +86,7 @@ func TestOracleParser(t *testing.T) {
|
|||||||
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
|
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
|
||||||
assert.Equal(t, "ELSA-2015-1207", vulnerabilities[0].Name)
|
assert.Equal(t, "ELSA-2015-1207", vulnerabilities[0].Name)
|
||||||
assert.Equal(t, "http://linux.oracle.com/errata/ELSA-2015-1207.html", vulnerabilities[0].Link)
|
assert.Equal(t, "http://linux.oracle.com/errata/ELSA-2015-1207.html", vulnerabilities[0].Link)
|
||||||
assert.Equal(t, types.Critical, vulnerabilities[0].Severity)
|
assert.Equal(t, clair.Critical, vulnerabilities[0].Severity)
|
||||||
assert.Equal(t, ` [38.1.0-1.0.1.el7_1] - Add firefox-oracle-default-prefs.js and remove the corresponding Red Hat file [38.1.0-1] - Update to 38.1.0 ESR [38.0.1-2] - Fixed rhbz#1222807 by removing preun section `, vulnerabilities[0].Description)
|
assert.Equal(t, ` [38.1.0-1.0.1.el7_1] - Add firefox-oracle-default-prefs.js and remove the corresponding Red Hat file [38.1.0-1] - Update to 38.1.0 ESR [38.0.1-2] - Fixed rhbz#1222807 by removing preun section `, vulnerabilities[0].Description)
|
||||||
expectedFeatureVersions := []database.FeatureVersion{
|
expectedFeatureVersions := []database.FeatureVersion{
|
||||||
{
|
{
|
||||||
|
@ -27,12 +27,12 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt"
|
"github.com/coreos/clair/ext/versionfmt"
|
||||||
"github.com/coreos/clair/ext/versionfmt/rpm"
|
"github.com/coreos/clair/ext/versionfmt/rpm"
|
||||||
"github.com/coreos/clair/ext/vulnsrc"
|
"github.com/coreos/clair/ext/vulnsrc"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -175,7 +175,7 @@ func parseRHSA(ovalReader io.Reader) (vulnerabilities []database.Vulnerability,
|
|||||||
vulnerability := database.Vulnerability{
|
vulnerability := database.Vulnerability{
|
||||||
Name: name(definition),
|
Name: name(definition),
|
||||||
Link: link(definition),
|
Link: link(definition),
|
||||||
Severity: priority(definition),
|
Severity: severity(definition),
|
||||||
Description: description(definition),
|
Description: description(definition),
|
||||||
}
|
}
|
||||||
for _, p := range pkgs {
|
for _, p := range pkgs {
|
||||||
@ -344,22 +344,18 @@ func link(def definition) (link string) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func priority(def definition) types.Priority {
|
func severity(def definition) clair.Severity {
|
||||||
// Parse the priority.
|
switch strings.TrimSpace(def.Title[strings.LastIndex(def.Title, "(")+1 : len(def.Title)-1]) {
|
||||||
priority := strings.TrimSpace(def.Title[strings.LastIndex(def.Title, "(")+1 : len(def.Title)-1])
|
|
||||||
|
|
||||||
// Normalize the priority.
|
|
||||||
switch priority {
|
|
||||||
case "Low":
|
case "Low":
|
||||||
return types.Low
|
return clair.Low
|
||||||
case "Moderate":
|
case "Moderate":
|
||||||
return types.Medium
|
return clair.Medium
|
||||||
case "Important":
|
case "Important":
|
||||||
return types.High
|
return clair.High
|
||||||
case "Critical":
|
case "Critical":
|
||||||
return types.Critical
|
return clair.Critical
|
||||||
default:
|
default:
|
||||||
log.Warning("could not determine vulnerability priority from: %s.", priority)
|
log.Warning("could not determine vulnerability severity from: %s.", def.Title)
|
||||||
return types.Unknown
|
return clair.Unknown
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 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.
|
||||||
@ -20,9 +20,9 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt/rpm"
|
"github.com/coreos/clair/ext/versionfmt/rpm"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -36,7 +36,7 @@ func TestRHELParser(t *testing.T) {
|
|||||||
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
|
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
|
||||||
assert.Equal(t, "RHSA-2015:1193", vulnerabilities[0].Name)
|
assert.Equal(t, "RHSA-2015:1193", vulnerabilities[0].Name)
|
||||||
assert.Equal(t, "https://rhn.redhat.com/errata/RHSA-2015-1193.html", vulnerabilities[0].Link)
|
assert.Equal(t, "https://rhn.redhat.com/errata/RHSA-2015-1193.html", vulnerabilities[0].Link)
|
||||||
assert.Equal(t, types.Medium, vulnerabilities[0].Severity)
|
assert.Equal(t, clair.Medium, vulnerabilities[0].Severity)
|
||||||
assert.Equal(t, `Xerces-C is a validating XML parser written in a portable subset of C++. A flaw was found in the way the Xerces-C XML parser processed certain XML documents. A remote attacker could provide specially crafted XML input that, when parsed by an application using Xerces-C, would cause that application to crash.`, vulnerabilities[0].Description)
|
assert.Equal(t, `Xerces-C is a validating XML parser written in a portable subset of C++. A flaw was found in the way the Xerces-C XML parser processed certain XML documents. A remote attacker could provide specially crafted XML input that, when parsed by an application using Xerces-C, would cause that application to crash.`, vulnerabilities[0].Description)
|
||||||
|
|
||||||
expectedFeatureVersions := []database.FeatureVersion{
|
expectedFeatureVersions := []database.FeatureVersion{
|
||||||
@ -83,7 +83,7 @@ func TestRHELParser(t *testing.T) {
|
|||||||
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
|
if assert.Nil(t, err) && assert.Len(t, vulnerabilities, 1) {
|
||||||
assert.Equal(t, "RHSA-2015:1207", vulnerabilities[0].Name)
|
assert.Equal(t, "RHSA-2015:1207", vulnerabilities[0].Name)
|
||||||
assert.Equal(t, "https://rhn.redhat.com/errata/RHSA-2015-1207.html", vulnerabilities[0].Link)
|
assert.Equal(t, "https://rhn.redhat.com/errata/RHSA-2015-1207.html", vulnerabilities[0].Link)
|
||||||
assert.Equal(t, types.Critical, vulnerabilities[0].Severity)
|
assert.Equal(t, clair.Critical, vulnerabilities[0].Severity)
|
||||||
assert.Equal(t, `Mozilla Firefox is an open source web browser. XULRunner provides the XUL Runtime environment for Mozilla Firefox. Several flaws were found in the processing of malformed web content. A web page containing malicious content could cause Firefox to crash or, potentially, execute arbitrary code with the privileges of the user running Firefox.`, vulnerabilities[0].Description)
|
assert.Equal(t, `Mozilla Firefox is an open source web browser. XULRunner provides the XUL Runtime environment for Mozilla Firefox. Several flaws were found in the processing of malformed web content. A web page containing malicious content could cause Firefox to crash or, potentially, execute arbitrary code with the privileges of the user running Firefox.`, vulnerabilities[0].Description)
|
||||||
|
|
||||||
expectedFeatureVersions := []database.FeatureVersion{
|
expectedFeatureVersions := []database.FeatureVersion{
|
||||||
|
@ -29,13 +29,13 @@ import (
|
|||||||
|
|
||||||
"github.com/coreos/pkg/capnslog"
|
"github.com/coreos/pkg/capnslog"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt"
|
"github.com/coreos/clair/ext/versionfmt"
|
||||||
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
"github.com/coreos/clair/ext/versionfmt/dpkg"
|
||||||
"github.com/coreos/clair/ext/vulnsrc"
|
"github.com/coreos/clair/ext/vulnsrc"
|
||||||
"github.com/coreos/clair/pkg/commonerr"
|
"github.com/coreos/clair/pkg/commonerr"
|
||||||
"github.com/coreos/clair/utils"
|
"github.com/coreos/clair/utils"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -301,7 +301,7 @@ func parseUbuntuCVE(fileContent io.Reader) (vulnerability database.Vulnerability
|
|||||||
priority = priority[:strings.Index(priority, " ")]
|
priority = priority[:strings.Index(priority, " ")]
|
||||||
}
|
}
|
||||||
|
|
||||||
vulnerability.Severity = ubuntuPriorityToSeverity(priority)
|
vulnerability.Severity = SeverityFromPriority(priority)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,28 +388,30 @@ func parseUbuntuCVE(fileContent io.Reader) (vulnerability database.Vulnerability
|
|||||||
|
|
||||||
// If no priority has been provided (CVE-2007-0667 for instance), set the priority to Unknown
|
// If no priority has been provided (CVE-2007-0667 for instance), set the priority to Unknown
|
||||||
if vulnerability.Severity == "" {
|
if vulnerability.Severity == "" {
|
||||||
vulnerability.Severity = types.Unknown
|
vulnerability.Severity = clair.Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func ubuntuPriorityToSeverity(priority string) types.Priority {
|
// SeverityFromPriority converts an priority from the Ubuntu CVE Tracker into
|
||||||
|
// a clair.Severity.
|
||||||
|
func SeverityFromPriority(priority string) clair.Severity {
|
||||||
switch priority {
|
switch priority {
|
||||||
case "untriaged":
|
case "untriaged":
|
||||||
return types.Unknown
|
return clair.Unknown
|
||||||
case "negligible":
|
case "negligible":
|
||||||
return types.Negligible
|
return clair.Negligible
|
||||||
case "low":
|
case "low":
|
||||||
return types.Low
|
return clair.Low
|
||||||
case "medium":
|
case "medium":
|
||||||
return types.Medium
|
return clair.Medium
|
||||||
case "high":
|
case "high":
|
||||||
return types.High
|
return clair.High
|
||||||
case "critical":
|
case "critical":
|
||||||
return types.Critical
|
return clair.Critical
|
||||||
|
default:
|
||||||
|
log.Warning("could not determine a vulnerability severity from: %s", priority)
|
||||||
|
return clair.Unknown
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Warning("Could not determine a vulnerability priority from: %s", priority)
|
|
||||||
return types.Unknown
|
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2016 clair authors
|
// Copyright 2017 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.
|
||||||
@ -22,9 +22,9 @@ import (
|
|||||||
|
|
||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/versionfmt"
|
"github.com/coreos/clair/ext/versionfmt"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUbuntuParser(t *testing.T) {
|
func TestUbuntuParser(t *testing.T) {
|
||||||
@ -37,7 +37,7 @@ func TestUbuntuParser(t *testing.T) {
|
|||||||
vulnerability, unknownReleases, err := parseUbuntuCVE(testData)
|
vulnerability, unknownReleases, err := parseUbuntuCVE(testData)
|
||||||
if assert.Nil(t, err) {
|
if assert.Nil(t, err) {
|
||||||
assert.Equal(t, "CVE-2015-4471", vulnerability.Name)
|
assert.Equal(t, "CVE-2015-4471", vulnerability.Name)
|
||||||
assert.Equal(t, types.Medium, vulnerability.Severity)
|
assert.Equal(t, clair.Medium, vulnerability.Severity)
|
||||||
assert.Equal(t, "Off-by-one error in the lzxd_decompress function in lzxd.c in libmspack before 0.5 allows remote attackers to cause a denial of service (buffer under-read and application crash) via a crafted CAB archive.", vulnerability.Description)
|
assert.Equal(t, "Off-by-one error in the lzxd_decompress function in lzxd.c in libmspack before 0.5 allows remote attackers to cause a denial of service (buffer under-read and application crash) via a crafted CAB archive.", vulnerability.Description)
|
||||||
|
|
||||||
// Unknown release (line 28)
|
// Unknown release (line 28)
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 clair authors
|
// Copyright 2017 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.
|
||||||
@ -12,80 +12,104 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
// Package types defines useful types that are used in database models.
|
package clair
|
||||||
package types
|
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"strings"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Priority defines a vulnerability priority
|
var (
|
||||||
type Priority string
|
// ErrFailedToParseSeverity is the error returned when a severity could not
|
||||||
|
// be parsed from a string.
|
||||||
|
ErrFailedToParseSeverity = errors.New("failed to parse Severity from input")
|
||||||
|
)
|
||||||
|
|
||||||
|
// Severity defines a standard scale for measuring the severity of a
|
||||||
|
// vulnerability.
|
||||||
|
type Severity string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// Unknown is either a security problem that has not been
|
// Unknown is either a security problem that has not been
|
||||||
// assigned to a priority yet or a priority that our system
|
// assigned to a priority yet or a priority that our system
|
||||||
// did not recognize
|
// did not recognize.
|
||||||
Unknown Priority = "Unknown"
|
Unknown Severity = "Unknown"
|
||||||
|
|
||||||
// Negligible is technically a security problem, but is
|
// Negligible is technically a security problem, but is
|
||||||
// only theoretical in nature, requires a very special
|
// only theoretical in nature, requires a very special
|
||||||
// situation, has almost no install base, or does no real
|
// situation, has almost no install base, or does no real
|
||||||
// damage. These tend not to get backport from upstreams,
|
// damage. These tend not to get backport from upstreams,
|
||||||
// and will likely not be included in security updates unless
|
// and will likely not be included in security updates unless
|
||||||
// there is an easy fix and some other issue causes an update.
|
// there is an easy fix and some other issue causes an update.
|
||||||
Negligible Priority = "Negligible"
|
Negligible Severity = "Negligible"
|
||||||
|
|
||||||
// Low is a security problem, but is hard to
|
// Low is a security problem, but is hard to
|
||||||
// exploit due to environment, requires a user-assisted
|
// exploit due to environment, requires a user-assisted
|
||||||
// attack, a small install base, or does very little damage.
|
// attack, a small install base, or does very little damage.
|
||||||
// These tend to be included in security updates only when
|
// These tend to be included in security updates only when
|
||||||
// higher priority issues require an update, or if many
|
// higher priority issues require an update, or if many
|
||||||
// low priority issues have built up.
|
// low priority issues have built up.
|
||||||
Low Priority = "Low"
|
Low Severity = "Low"
|
||||||
|
|
||||||
// Medium is a real security problem, and is exploitable
|
// Medium is a real security problem, and is exploitable
|
||||||
// for many people. Includes network daemon denial of service
|
// for many people. Includes network daemon denial of service
|
||||||
// attacks, cross-site scripting, and gaining user privileges.
|
// attacks, cross-site scripting, and gaining user privileges.
|
||||||
// Updates should be made soon for this priority of issue.
|
// Updates should be made soon for this priority of issue.
|
||||||
Medium Priority = "Medium"
|
Medium Severity = "Medium"
|
||||||
|
|
||||||
// High is a real problem, exploitable for many people in a default
|
// High is a real problem, exploitable for many people in a default
|
||||||
// installation. Includes serious remote denial of services,
|
// installation. Includes serious remote denial of services,
|
||||||
// local root privilege escalations, or data loss.
|
// local root privilege escalations, or data loss.
|
||||||
High Priority = "High"
|
High Severity = "High"
|
||||||
|
|
||||||
// Critical is a world-burning problem, exploitable for nearly all people
|
// Critical is a world-burning problem, exploitable for nearly all people
|
||||||
// in a default installation of Linux. Includes remote root
|
// in a default installation of Linux. Includes remote root
|
||||||
// privilege escalations, or massive data loss.
|
// privilege escalations, or massive data loss.
|
||||||
Critical Priority = "Critical"
|
Critical Severity = "Critical"
|
||||||
|
|
||||||
// Defcon1 is a Critical problem which has been manually highlighted by
|
// Defcon1 is a Critical problem which has been manually highlighted by
|
||||||
// the team. It requires an immediate attention.
|
// the team. It requires an immediate attention.
|
||||||
Defcon1 Priority = "Defcon1"
|
Defcon1 Severity = "Defcon1"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Priorities lists all known priorities, ordered from lower to higher
|
// Severities lists all known severities, ordered from lowest to highest.
|
||||||
var Priorities = []Priority{Unknown, Negligible, Low, Medium, High, Critical, Defcon1}
|
var Severities = []Severity{
|
||||||
|
Unknown,
|
||||||
|
Negligible,
|
||||||
|
Low,
|
||||||
|
Medium,
|
||||||
|
High,
|
||||||
|
Critical,
|
||||||
|
Defcon1,
|
||||||
|
}
|
||||||
|
|
||||||
// IsValid determines if the priority is a valid one
|
// NewSeverity attempts to parse a string into a standard Severity value.
|
||||||
func (p Priority) IsValid() bool {
|
func NewSeverity(s string) (Severity, error) {
|
||||||
for _, pp := range Priorities {
|
for _, ss := range Severities {
|
||||||
if p == pp {
|
if strings.EqualFold(s, string(ss)) {
|
||||||
return true
|
return ss, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return false
|
return Unknown, ErrFailedToParseSeverity
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare compares two priorities
|
// Compare determines the equality of two severities.
|
||||||
func (p Priority) Compare(p2 Priority) int {
|
//
|
||||||
|
// If the severities are equal, returns 0.
|
||||||
|
// If the receiever is less, returns -1.
|
||||||
|
// If the receiver is greater, returns 1.
|
||||||
|
func (s Severity) Compare(s2 Severity) int {
|
||||||
var i1, i2 int
|
var i1, i2 int
|
||||||
|
|
||||||
for i1 = 0; i1 < len(Priorities); i1 = i1 + 1 {
|
for i1 = 0; i1 < len(Severities); i1 = i1 + 1 {
|
||||||
if p == Priorities[i1] {
|
if s == Severities[i1] {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i2 = 0; i2 < len(Priorities); i2 = i2 + 1 {
|
for i2 = 0; i2 < len(Severities); i2 = i2 + 1 {
|
||||||
if p2 == Priorities[i2] {
|
if s2 == Severities[i2] {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -93,18 +117,23 @@ func (p Priority) Compare(p2 Priority) int {
|
|||||||
return i1 - i2
|
return i1 - i2
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Priority) Scan(value interface{}) error {
|
// Scan implements the database/sql.Scanner interface.
|
||||||
|
func (s *Severity) Scan(value interface{}) error {
|
||||||
val, ok := value.([]byte)
|
val, ok := value.([]byte)
|
||||||
if !ok {
|
if !ok {
|
||||||
return errors.New("could not scan a Priority from a non-string input")
|
return errors.New("could not scan a Severity from a non-string input")
|
||||||
}
|
}
|
||||||
*p = Priority(string(val))
|
|
||||||
if !p.IsValid() {
|
var err error
|
||||||
return fmt.Errorf("could not scan an invalid Priority (%v)", p)
|
*s, err = NewSeverity(string(val))
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Priority) Value() (driver.Value, error) {
|
// Value implements the database/sql/driver.Valuer interface.
|
||||||
return string(*p), nil
|
func (s Severity) Value() (driver.Value, error) {
|
||||||
|
return string(s), nil
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
// Copyright 2015 clair authors
|
// Copyright 2017 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.
|
||||||
@ -12,7 +12,7 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
package types
|
package clair
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"testing"
|
"testing"
|
||||||
@ -20,13 +20,16 @@ import (
|
|||||||
"github.com/stretchr/testify/assert"
|
"github.com/stretchr/testify/assert"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestComparePriority(t *testing.T) {
|
func TestCompareSeverity(t *testing.T) {
|
||||||
assert.Equal(t, Medium.Compare(Medium), 0, "Priority comparison failed")
|
assert.Equal(t, Medium.Compare(Medium), 0, "Severity comparison failed")
|
||||||
assert.True(t, Medium.Compare(High) < 0, "Priority comparison failed")
|
assert.True(t, Medium.Compare(High) < 0, "Severity comparison failed")
|
||||||
assert.True(t, Critical.Compare(Low) > 0, "Priority comparison failed")
|
assert.True(t, Critical.Compare(Low) > 0, "Severity comparison failed")
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestIsValid(t *testing.T) {
|
func TestParseSeverity(t *testing.T) {
|
||||||
assert.False(t, Priority("Test").IsValid())
|
_, err := NewSeverity("Test")
|
||||||
assert.True(t, Unknown.IsValid())
|
assert.Equal(t, ErrFailedToParseSeverity, err)
|
||||||
|
|
||||||
|
_, err = NewSeverity("Unknown")
|
||||||
|
assert.Nil(t, err)
|
||||||
}
|
}
|
@ -27,12 +27,12 @@ import (
|
|||||||
"github.com/pborman/uuid"
|
"github.com/pborman/uuid"
|
||||||
"github.com/prometheus/client_golang/prometheus"
|
"github.com/prometheus/client_golang/prometheus"
|
||||||
|
|
||||||
|
"github.com/coreos/clair"
|
||||||
"github.com/coreos/clair/config"
|
"github.com/coreos/clair/config"
|
||||||
"github.com/coreos/clair/database"
|
"github.com/coreos/clair/database"
|
||||||
"github.com/coreos/clair/ext/vulnmdsrc"
|
"github.com/coreos/clair/ext/vulnmdsrc"
|
||||||
"github.com/coreos/clair/ext/vulnsrc"
|
"github.com/coreos/clair/ext/vulnsrc"
|
||||||
"github.com/coreos/clair/utils"
|
"github.com/coreos/clair/utils"
|
||||||
"github.com/coreos/clair/utils/types"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -316,7 +316,7 @@ type lockableVulnerability struct {
|
|||||||
sync.Mutex
|
sync.Mutex
|
||||||
}
|
}
|
||||||
|
|
||||||
func (lv *lockableVulnerability) appendFunc(metadataKey string, metadata interface{}, severity types.Priority) {
|
func (lv *lockableVulnerability) appendFunc(metadataKey string, metadata interface{}, severity clair.Severity) {
|
||||||
lv.Lock()
|
lv.Lock()
|
||||||
defer lv.Unlock()
|
defer lv.Unlock()
|
||||||
|
|
||||||
@ -329,7 +329,7 @@ func (lv *lockableVulnerability) appendFunc(metadataKey string, metadata interfa
|
|||||||
lv.Metadata[metadataKey] = metadata
|
lv.Metadata[metadataKey] = metadata
|
||||||
|
|
||||||
// If necessary, provide a severity for the vulnerability.
|
// If necessary, provide a severity for the vulnerability.
|
||||||
if lv.Severity == "" || lv.Severity == types.Unknown {
|
if lv.Severity == clair.Unknown {
|
||||||
lv.Severity = severity
|
lv.Severity = severity
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user