ext/featurefmt/dpkg: Extract source package metadata

The source package metadata is extracted from the source line instead
of forcing the binary package to have source package information.
master
Sida Chen 6 years ago
parent 1c40e7d016
commit 4ac046642f

@ -20,6 +20,7 @@ import (
"regexp" "regexp"
"strings" "strings"
"github.com/deckarep/golang-set"
log "github.com/sirupsen/logrus" log "github.com/sirupsen/logrus"
"github.com/coreos/clair/database" "github.com/coreos/clair/database"
@ -40,29 +41,39 @@ func init() {
featurefmt.RegisterLister("dpkg", "1.0", &lister{}) featurefmt.RegisterLister("dpkg", "1.0", &lister{})
} }
func valid(pkg *featurefmt.PackageInfo) bool {
return pkg.PackageName != "" && pkg.PackageVersion != ""
}
func addSourceVersion(pkg *featurefmt.PackageInfo) {
if pkg.SourceName != "" && pkg.SourceVersion == "" {
pkg.SourceVersion = pkg.PackageVersion
}
}
func (l lister) ListFeatures(files tarutil.FilesMap) ([]database.Feature, error) { func (l lister) ListFeatures(files tarutil.FilesMap) ([]database.Feature, error) {
f, hasFile := files["var/lib/dpkg/status"] f, hasFile := files["var/lib/dpkg/status"]
if !hasFile { if !hasFile {
return []database.Feature{}, nil return []database.Feature{}, nil
} }
// Create a map to store packages and ensure their uniqueness var (
packagesMap := make(map[string]database.Feature) pkg featurefmt.PackageInfo
pkgs = mapset.NewSet()
err error
)
var pkg database.Feature
var err error
scanner := bufio.NewScanner(strings.NewReader(string(f))) scanner := bufio.NewScanner(strings.NewReader(string(f)))
for scanner.Scan() { for scanner.Scan() {
line := scanner.Text() line := scanner.Text()
if strings.HasPrefix(line, "Package: ") { if strings.HasPrefix(line, "Package: ") {
// Package line // Package line
// Defines the name of the package // Defines the name of the package
pkg.Name = strings.TrimSpace(strings.TrimPrefix(line, "Package: ")) pkg.PackageName = strings.TrimSpace(strings.TrimPrefix(line, "Package: "))
pkg.Version = "" pkg.PackageVersion = ""
} else if strings.HasPrefix(line, "Source: ") { } else if strings.HasPrefix(line, "Source: ") {
// Source line (Optionnal) // Source line (Optional)
// Gives the name of the source package // Gives the name of the source package
// May also specifies a version // May also specifies a version
@ -72,50 +83,38 @@ func (l lister) ListFeatures(files tarutil.FilesMap) ([]database.Feature, error)
md[dpkgSrcCaptureRegexpNames[i]] = strings.TrimSpace(n) md[dpkgSrcCaptureRegexpNames[i]] = strings.TrimSpace(n)
} }
pkg.Name = md["name"] pkg.SourceName = md["name"]
if md["version"] != "" { if md["version"] != "" {
version := md["version"] version := md["version"]
err = versionfmt.Valid(dpkg.ParserName, version) if err = versionfmt.Valid(dpkg.ParserName, version); err != nil {
if err != nil {
log.WithError(err).WithField("version", string(line[1])).Warning("could not parse package version. skipping") log.WithError(err).WithField("version", string(line[1])).Warning("could not parse package version. skipping")
} else { } else {
pkg.Version = version pkg.SourceVersion = version
} }
} }
} else if strings.HasPrefix(line, "Version: ") && pkg.Version == "" { } else if strings.HasPrefix(line, "Version: ") {
// Version line // Version line
// Defines the version of the package // Defines the version of the package
// This version is less important than a version retrieved from a Source line // This version is less important than a version retrieved from a Source line
// because the Debian vulnerabilities often skips the epoch from the Version field // because the Debian vulnerabilities often skips the epoch from the Version field
// which is not present in the Source version, and because +bX revisions don't matter // which is not present in the Source version, and because +bX revisions don't matter
version := strings.TrimPrefix(line, "Version: ") version := strings.TrimPrefix(line, "Version: ")
err = versionfmt.Valid(dpkg.ParserName, version) if err = versionfmt.Valid(dpkg.ParserName, version); err != nil {
if err != nil {
log.WithError(err).WithField("version", string(line[1])).Warning("could not parse package version. skipping") log.WithError(err).WithField("version", string(line[1])).Warning("could not parse package version. skipping")
} else { } else {
pkg.Version = version pkg.PackageVersion = version
} }
} else if line == "" { } else if line == "" {
pkg.Name = "" pkg.Reset()
pkg.Version = ""
} }
// Add the package to the result array if we have all the informations if valid(&pkg) {
if pkg.Name != "" && pkg.Version != "" { addSourceVersion(&pkg)
packagesMap[pkg.Name+"#"+pkg.Version] = pkg pkgs.Add(pkg)
pkg.Name = ""
pkg.Version = ""
} }
} }
// Convert the map to a slice and add version format. return featurefmt.PackageSetToFeatures(dpkg.ParserName, pkgs), nil
packages := make([]database.Feature, 0, len(packagesMap))
for _, pkg := range packagesMap {
pkg.VersionFormat = dpkg.ParserName
packages = append(packages, pkg)
}
return packages, nil
} }
func (l lister) RequiredFilenames() []string { func (l lister) RequiredFilenames() []string {

@ -17,42 +17,118 @@ package dpkg
import ( import (
"testing" "testing"
"github.com/coreos/clair/database"
"github.com/coreos/clair/ext/featurefmt" "github.com/coreos/clair/ext/featurefmt"
"github.com/coreos/clair/ext/versionfmt/dpkg" "github.com/coreos/clair/ext/versionfmt/dpkg"
"github.com/coreos/clair/pkg/tarutil"
) )
func TestDpkgFeatureDetection(t *testing.T) { func TestListFeatures(t *testing.T) {
testFeatures := []database.Feature{ for _, test := range []featurefmt.TestCase{
// Two packages from this source are installed, it should only appear one time
{ {
Name: "pam", "valid status file",
Version: "1.1.8-3.1ubuntu3", map[string]string{"var/lib/dpkg/status": "dpkg/testdata/valid"},
}, []featurefmt.PackageInfo{
{ {"adduser", "3.116ubuntu1", "", ""},
Name: "makedev", // The source name and the package name are equals {"apt", "1.6.3ubuntu0.1", "", ""},
Version: "2.3.1-93ubuntu1", // The version comes from the "Version:" line {"base-files", "10.1ubuntu2.2", "", ""},
}, {"base-passwd", "3.5.44", "", ""},
{ {"bash", "4.4.18-2ubuntu1", "", ""},
Name: "gcc-5", {"bsdutils", "1:2.31.1-0.4ubuntu3.1", "util-linux", "2.31.1-0.4ubuntu3.1"},
Version: "5.1.1-12ubuntu1", // The version comes from the "Source:" line {"bzip2", "1.0.6-8.1", "", ""},
{"coreutils", "8.28-1ubuntu1", "", ""},
{"dash", "0.5.8-2.10", "", ""},
{"debconf", "1.5.66", "", ""},
{"debianutils", "4.8.4", "", ""},
{"diffutils", "1:3.6-1", "", ""},
{"dpkg", "1.19.0.5ubuntu2", "", ""},
{"e2fsprogs", "1.44.1-1", "", ""},
{"fdisk", "2.31.1-0.4ubuntu3.1", "util-linux", "2.31.1-0.4ubuntu3.1"},
{"findutils", "4.6.0+git+20170828-2", "", ""},
{"gcc-8-base", "8-20180414-1ubuntu2", "gcc-8", "8-20180414-1ubuntu2"},
{"gpgv", "2.2.4-1ubuntu1.1", "gnupg2", "2.2.4-1ubuntu1.1"},
{"grep", "3.1-2", "", ""},
{"gzip", "1.6-5ubuntu1", "", ""},
{"hostname", "3.20", "", ""},
{"init-system-helpers", "1.51", "", ""},
{"libacl1", "2.2.52-3build1", "acl", "2.2.52-3build1"},
{"libapt-pkg5.0", "1.6.3ubuntu0.1", "apt", "1.6.3ubuntu0.1"},
{"libattr1", "1:2.4.47-2build1", "attr", "1:2.4.47-2build1"},
{"libaudit-common", "1:2.8.2-1ubuntu1", "audit", "1:2.8.2-1ubuntu1"},
{"libaudit1", "1:2.8.2-1ubuntu1", "audit", "1:2.8.2-1ubuntu1"},
{"libblkid1", "2.31.1-0.4ubuntu3.1", "util-linux", "2.31.1-0.4ubuntu3.1"},
{"libbz2-1.0", "1.0.6-8.1", "bzip2", "1.0.6-8.1"},
{"libc-bin", "2.27-3ubuntu1", "glibc", "2.27-3ubuntu1"},
{"libc6", "2.27-3ubuntu1", "glibc", "2.27-3ubuntu1"},
{"libcap-ng0", "0.7.7-3.1", "libcap-ng", "0.7.7-3.1"},
{"libcom-err2", "1.44.1-1", "e2fsprogs", "1.44.1-1"},
{"libdb5.3", "5.3.28-13.1ubuntu1", "db5.3", "5.3.28-13.1ubuntu1"},
{"libdebconfclient0", "0.213ubuntu1", "cdebconf", "0.213ubuntu1"},
{"libext2fs2", "1.44.1-1", "e2fsprogs", "1.44.1-1"},
{"libfdisk1", "2.31.1-0.4ubuntu3.1", "util-linux", "2.31.1-0.4ubuntu3.1"},
{"libffi6", "3.2.1-8", "libffi", "3.2.1-8"},
{"libgcc1", "1:8-20180414-1ubuntu2", "gcc-8", "8-20180414-1ubuntu2"},
{"libgcrypt20", "1.8.1-4ubuntu1.1", "", ""},
{"libgmp10", "2:6.1.2+dfsg-2", "gmp", "2:6.1.2+dfsg-2"},
{"libgnutls30", "3.5.18-1ubuntu1", "gnutls28", "3.5.18-1ubuntu1"},
{"libgpg-error0", "1.27-6", "libgpg-error", "1.27-6"},
{"libhogweed4", "3.4-1", "nettle", "3.4-1"},
{"libidn2-0", "2.0.4-1.1build2", "libidn2", "2.0.4-1.1build2"},
{"liblz4-1", "0.0~r131-2ubuntu3", "lz4", "0.0~r131-2ubuntu3"},
{"liblzma5", "5.2.2-1.3", "xz-utils", "5.2.2-1.3"},
{"libmount1", "2.31.1-0.4ubuntu3.1", "util-linux", "2.31.1-0.4ubuntu3.1"},
{"libncurses5", "6.1-1ubuntu1.18.04", "ncurses", "6.1-1ubuntu1.18.04"},
{"libncursesw5", "6.1-1ubuntu1.18.04", "ncurses", "6.1-1ubuntu1.18.04"},
{"libnettle6", "3.4-1", "nettle", "3.4-1"},
{"libp11-kit0", "0.23.9-2", "p11-kit", "0.23.9-2"},
{"libpam-modules", "1.1.8-3.6ubuntu2", "pam", "1.1.8-3.6ubuntu2"},
{"libpam-modules-bin", "1.1.8-3.6ubuntu2", "pam", "1.1.8-3.6ubuntu2"},
{"libpam-runtime", "1.1.8-3.6ubuntu2", "pam", "1.1.8-3.6ubuntu2"},
{"libpam0g", "1.1.8-3.6ubuntu2", "pam", "1.1.8-3.6ubuntu2"},
{"libpcre3", "2:8.39-9", "pcre3", "2:8.39-9"},
{"libprocps6", "2:3.3.12-3ubuntu1.1", "procps", "2:3.3.12-3ubuntu1.1"},
{"libseccomp2", "2.3.1-2.1ubuntu4", "libseccomp", "2.3.1-2.1ubuntu4"},
{"libselinux1", "2.7-2build2", "libselinux", "2.7-2build2"},
{"libsemanage-common", "2.7-2build2", "libsemanage", "2.7-2build2"},
{"libsemanage1", "2.7-2build2", "libsemanage", "2.7-2build2"},
{"libsepol1", "2.7-1", "libsepol", "2.7-1"},
{"libsmartcols1", "2.31.1-0.4ubuntu3.1", "util-linux", "2.31.1-0.4ubuntu3.1"},
{"libss2", "1.44.1-1", "e2fsprogs", "1.44.1-1"},
{"libstdc++6", "8-20180414-1ubuntu2", "gcc-8", "8-20180414-1ubuntu2"},
{"libsystemd0", "237-3ubuntu10.3", "systemd", "237-3ubuntu10.3"},
{"libtasn1-6", "4.13-2", "", ""},
{"libtinfo5", "6.1-1ubuntu1.18.04", "ncurses", "6.1-1ubuntu1.18.04"},
{"libudev1", "237-3ubuntu10.3", "systemd", "237-3ubuntu10.3"},
{"libunistring2", "0.9.9-0ubuntu1", "libunistring", "0.9.9-0ubuntu1"},
{"libuuid1", "2.31.1-0.4ubuntu3.1", "util-linux", "2.31.1-0.4ubuntu3.1"},
{"libzstd1", "1.3.3+dfsg-2ubuntu1", "libzstd", "1.3.3+dfsg-2ubuntu1"},
{"login", "1:4.5-1ubuntu1", "shadow", "1:4.5-1ubuntu1"},
{"lsb-base", "9.20170808ubuntu1", "lsb", "9.20170808ubuntu1"},
{"mawk", "1.3.3-17ubuntu3", "", ""},
{"mount", "2.31.1-0.4ubuntu3.1", "util-linux", "2.31.1-0.4ubuntu3.1"},
{"ncurses-base", "6.1-1ubuntu1.18.04", "ncurses", "6.1-1ubuntu1.18.04"},
{"ncurses-bin", "6.1-1ubuntu1.18.04", "ncurses", "6.1-1ubuntu1.18.04"},
{"passwd", "1:4.5-1ubuntu1", "shadow", "1:4.5-1ubuntu1"},
{"perl-base", "5.26.1-6ubuntu0.2", "perl", "5.26.1-6ubuntu0.2"},
{"procps", "2:3.3.12-3ubuntu1.1", "", ""},
{"sed", "4.4-2", "", ""},
{"sensible-utils", "0.0.12", "", ""},
{"sysvinit-utils", "2.88dsf-59.10ubuntu1", "sysvinit", "2.88dsf-59.10ubuntu1"},
{"tar", "1.29b-2", "", ""},
{"ubuntu-keyring", "2018.02.28", "", ""},
{"util-linux", "2.31.1-0.4ubuntu3.1", "", ""},
{"zlib1g", "1:1.2.11.dfsg-0ubuntu2", "zlib", "1:1.2.11.dfsg-0ubuntu2"},
},
}, },
}
for i := range testFeatures {
testFeatures[i].VersionFormat = dpkg.ParserName
}
testData := []featurefmt.TestData{
// Test an Ubuntu dpkg status file
{ {
Features: testFeatures, "corrupted status file",
Files: tarutil.FilesMap{ map[string]string{"var/lib/dpkg/status": "dpkg/testdata/corrupted"},
"var/lib/dpkg/status": featurefmt.LoadFileForTest("dpkg/testdata/status"), []featurefmt.PackageInfo{
{"libpam-runtime", "1.1.8-3.1ubuntu3", "pam", "1.1.8-3.1ubuntu3"},
{"libpam-modules-bin", "1.1.8-3.1ubuntu3", "pam", "1.1.8-3.1ubuntu3"},
{"makedev", "2.3.1-93ubuntu1", "", ""},
{"libgcc1", "1:5.1.1-12ubuntu1", "gcc-5", "5.1.1-12ubuntu1"},
}, },
}, },
} {
featurefmt.RunTest(t, test, &lister{}, dpkg.ParserName)
} }
featurefmt.TestLister(t, &lister{}, testData)
} }

File diff suppressed because it is too large Load Diff
Loading…
Cancel
Save