Vulnerability Matching
This is a high level overview of the interfaces that must be implemented for end-to-end vulnerability matching.
A description of the end-to-end process is described as well.
Scanners
Several Scanner interface types exist for extracting contents from container layers.
PackageScanner
claircore/internal/indexer/PackageScanner
type PackageScanner interface {
VersionedScanner
// Scan performs a package scan on the given layer and returns all
// the found packages
Scan(*claircore.Layer) ([]*claircore.Package, error)
}
type VersionedScanner interface {
// unique name of the distribution scanner.
Name() string
// version of this scanner. this information will be persisted with the scan.
Version() string
// the kind of scanner. currently only package is implemented
Kind() string
}
A PackageScanner implementation should parse a discovered package database within the provided layer and return an array of claircore.Package
structures reflecting the parsed packages.
DistributionScanner
claircore/internal/indexer/DistributionScanner
type DistributionScanner interface {
VersionedScanner
Scan(context.Context, *claircore.Layer) ([]*claircore.Distribution, error)
}
A DistributionScanner implementation should discover the Distribution information of layer.
Distribution is typically the base operating system the layer demonstrates features of.
RepositoryScanner (currently not in use)
claircore/internal/indexer/RepositoryScanner
type RepositoryScanner interface {
VersionedScanner
Scan(context.Context, *claircore.Layer) ([]*claircore.Repository, error)
}
A RepositoryScanner implementation should discover any package repositories present in the layer.
This is currently not implemented however future plans are to match packages with their owning repository.
Updater
claircore/libvuln/driver/Updater
type Updater interface {
Name() string
Fetcher
Parser
}
An Updater implementation is responsible for fetching a security advisory database and parsing the contents.
An Updater is an aggregate interface consisting of.
claircore/libvuln/driver/Fetcher
claircore/libvuln/driver/Parser
type Fingerprint string
type Fetcher interface {
Fetch(context.Context, Fingerprint) (io.ReadCloser, Fingerprint, error)
}
A Fetcher
implementation is responsible for returning an io.ReadCloser where the contents of a security database can be read from.
A Fingerprint is provided so the implementation can determine if the security database needs to be fetched.
For example the Fingerprint maybe a sha-256 hash of the contents.
See source for mode details.
type Parser interface {
Parse(ctx context.Context, contents io.ReadCloser) ([]*claircore.Vulnerability, error)
}
The reason we split fetching and parsing is to easily support offline modes of operation.
A parser can be provided any io.ReadCloser allowing for simple scripts to be implemented for on demand parsing and indexing of CVE data.
In order to run your updater on an interval and as part of the claircore runtime you must implement both methods.
Matcher
claircore/libvuln/driver/Matcher
type Matcher interface {
Name() string
Filter(record *claircore.IndexRecord) bool
Query() []MatchConstraint
Vulnerable(ctx context.Context, record *claircore.IndexRecord, vuln *claircore.Vulnerability) (bool, error)
}
A Matcher
implementation is responsible for telling ClairCore which packages to query via the Filter
method, how to query the security advisory database via the Query
method and whether the discovered Vulnerability
from the security advisory database affects the provided package via the Vulnerable
method.
See implementations for further details.
A Matcher
implementation should exist next to a Updater
implementation to share this information between the two.
A Matcher
informs ClairCore how to query the security advisory database by returning a list of MatchConstraint
.
A MatchContraint
constrains a query to the security advisory database by the provided values.
Multiple MatchConstraint
will be 'AND'd together.
type MatchConstraint int
const (
_ MatchConstraint = iota
DistributionDID
DistributionName
DistributionVersion
DistributionVersionCodeName
DistributionVersionID
DistributionArch
DistributionCPE
DistributionPrettyName
)
As an example the Ubuntu Updater
parses and indexes vulnerabilities and populates the Vulnerability.Distribution.DID
, Vulnerability.Distribution.Name
, and Vulnerability.Distribution.Version
fields.
The Ubuntu Matcher
is aware of this and constrains it's queries by returning DistributionDID
,DistributionName
, DistributionVersion
, constraints when it's Query
method is called.
ClairCore will query the security advisory database with these constraints returning only applicable vulnerabilities.
An end to end success
A successful scan looks like this:
Updaters
have ran either in the background on an interval or have had theirParse
methods called and an offline-load placed CVE data into the security advisory database.- A
Manifest
is provided tolibindex
.libindex
fetches all the layers, runs all scanner types on each layer, persists all artifacts found in each layer, and computes an IndexReport. - A
IndexReport
is provided tolibvuln
. libvuln
creates a stream ofIndexRecord
structs from the IndexReport and concurrently streams these structs to each configuredMatcher
.libvuln
computes aVulnerabilityReport
aggregating all vulnerabilities discovered by all configuredMatcher
implementations.- Sometime later the security advisory database is updated and a new request to
libvuln
will present updated vulnerability data.