diff --git a/binary/proto/proto.go b/binary/proto/proto.go index 562a82db..ebef5fe5 100644 --- a/binary/proto/proto.go +++ b/binary/proto/proto.go @@ -257,6 +257,7 @@ func setProtoMetadata(meta any, i *spb.Inventory) { DpkgMetadata: &spb.DPKGPackageMetadata{ PackageName: m.PackageName, SourceName: m.SourceName, + Status: m.Status, SourceVersion: m.SourceVersion, PackageVersion: m.PackageVersion, OsId: m.OSID, diff --git a/binary/proto/scan_result.proto b/binary/proto/scan_result.proto index 90b62969..d430aeb4 100644 --- a/binary/proto/scan_result.proto +++ b/binary/proto/scan_result.proto @@ -200,6 +200,7 @@ message APKPackageMetadata { } // The additional data found in DPKG packages. +// Next ID: 11 message DPKGPackageMetadata { string package_name = 1; string source_name = 2; @@ -210,6 +211,7 @@ message DPKGPackageMetadata { string os_version_id = 7; string maintainer = 8; string architecture = 9; + string status = 10; } // The additional data found in RPM packages. diff --git a/extractor/os/dpkg/extractor.go b/extractor/os/dpkg/extractor.go index 65e4435a..48886268 100644 --- a/extractor/os/dpkg/extractor.go +++ b/extractor/os/dpkg/extractor.go @@ -125,17 +125,7 @@ func (e Extractor) Extract(ctx context.Context, input *extractor.ScanInput) ([]* return pkgs, err } } - // Distroless distributions have their packages in status.d, which does not contain the Status - // value. - if !strings.Contains(input.Path, "status.d") || h.Get("Status") != "" { - installed, err := statusInstalled(h.Get("Status")) - if err != nil { - return pkgs, fmt.Errorf("statusInstalled(%q): %w", h.Get("Status"), err) - } - if !installed { - continue - } - } + pkgName := h.Get("Package") pkgVersion := h.Get("Version") if pkgName == "" || pkgVersion == "" { @@ -153,6 +143,7 @@ func (e Extractor) Extract(ctx context.Context, input *extractor.ScanInput) ([]* Metadata: &Metadata{ PackageName: pkgName, PackageVersion: pkgVersion, + Status: h.Get("Status"), OSID: m["ID"], OSVersionCodename: m["VERSION_CODENAME"], OSVersionID: m["VERSION_ID"], @@ -176,17 +167,6 @@ func (e Extractor) Extract(ctx context.Context, input *extractor.ScanInput) ([]* return pkgs, nil } -func statusInstalled(status string) (bool, error) { - // Status field format: "want flag status", e.g. "install ok installed" - // The package is currently installed if the status field is set to installed. - // Other fields just show the intent of the package manager but not the current state. - parts := strings.Split(status, " ") - if len(parts) != 3 { - return false, fmt.Errorf("invalid DPKG Status field %q", status) - } - return parts[2] == "installed", nil -} - func parseSourceNameVersion(source string) (string, string, error) { if source == "" { return "", "", nil diff --git a/extractor/os/dpkg/extractor_test.go b/extractor/os/dpkg/extractor_test.go index 9f7f180a..c0f7199d 100644 --- a/extractor/os/dpkg/extractor_test.go +++ b/extractor/os/dpkg/extractor_test.go @@ -90,6 +90,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "accountsservice", PackageVersion: "22.08.8-6", + Status: "install ok installed", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -105,6 +106,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "acl", PackageVersion: "2.3.1-3", + Status: "install ok installed", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -120,6 +122,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "adduser", PackageVersion: "3.131", + Status: "install ok installed", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -135,6 +138,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "admin-session", PackageVersion: "2023.06.26.c543406313-00", + Status: "install ok installed", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -150,6 +154,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "attr", PackageVersion: "1:2.5.1-4", + Status: "install ok installed", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -166,6 +171,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "libacl1", PackageVersion: "2.3.1-3", + Status: "install ok installed", SourceName: "acl", OSID: "debian", OSVersionCodename: "bookworm", @@ -183,6 +189,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "util-linux-extra", PackageVersion: "2.38.1-5+b1", + Status: "install ok installed", SourceName: "util-linux", SourceVersion: "2.38.1-5", OSID: "debian", @@ -207,6 +214,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "foo", PackageVersion: "1.0", + Status: "install ok installed", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -220,6 +228,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "bar", PackageVersion: "2.0", + Status: "install ok installed", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -240,6 +249,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "foo", PackageVersion: "1.0", + Status: "install ok installed", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -253,6 +263,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "bar", PackageVersion: "2.0", + Status: "install ok installed", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -273,6 +284,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "wantinstall_installed", PackageVersion: "1.0", + Status: "install ok installed", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -286,6 +298,35 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "wantdeinstall_installed", PackageVersion: "1.0", + Status: "deinstall reinstreq installed", + OSID: "debian", + OSVersionCodename: "bookworm", + OSVersionID: "12", + }, + Locations: []string{"testdata/statusfield"}, + Extractor: dpkg.Name, + }, + &extractor.Inventory{ + Name: "wantdeinstall_configfiles", + Version: "1.0", + Metadata: &dpkg.Metadata{ + PackageName: "wantdeinstall_configfiles", + PackageVersion: "1.0", + Status: "deinstall ok config-files", + OSID: "debian", + OSVersionCodename: "bookworm", + OSVersionID: "12", + }, + Locations: []string{"testdata/statusfield"}, + Extractor: dpkg.Name, + }, + &extractor.Inventory{ + Name: "wantinstall_unpacked", + Version: "1.0", + Metadata: &dpkg.Metadata{ + PackageName: "wantinstall_unpacked", + PackageVersion: "1.0", + Status: "install ok unpacked", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -299,6 +340,34 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "wantpurge_installed", PackageVersion: "1.0", + Status: "purge ok installed", + OSID: "debian", + OSVersionCodename: "bookworm", + OSVersionID: "12", + }, + Locations: []string{"testdata/statusfield"}, + Extractor: dpkg.Name, + }, + &extractor.Inventory{ + Name: "wantinstall_halfinstalled", + Version: "1.0", + Metadata: &dpkg.Metadata{ + PackageName: "wantinstall_halfinstalled", + PackageVersion: "1.0", + Status: "install reinstreq half-installed", + OSID: "debian", + OSVersionCodename: "bookworm", + OSVersionID: "12", + }, + Locations: []string{"testdata/statusfield"}, + Extractor: dpkg.Name, + }, + &extractor.Inventory{ + Name: "wantnostatus", + Version: "1.0", + Metadata: &dpkg.Metadata{ + PackageName: "wantnostatus", + PackageVersion: "1.0", OSID: "debian", OSVersionCodename: "bookworm", OSVersionID: "12", @@ -313,7 +382,6 @@ func TestExtract(t *testing.T) { path: "testdata/empty", osrelease: DebianBookworm, wantInventory: []*extractor.Inventory{}, - wantErr: cmpopts.AnyError, }, { name: "invalid", @@ -334,6 +402,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "acl", PackageVersion: "2.3.1-3", + Status: "install ok installed", OSID: "debian", OSVersionID: "12", Maintainer: "Guillem Jover ", @@ -355,6 +424,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "acl", PackageVersion: "2.3.1-3", + Status: "install ok installed", OSID: "debian", Maintainer: "Guillem Jover ", Architecture: "amd64", @@ -375,6 +445,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "acl", PackageVersion: "2.3.1-3", + Status: "install ok installed", OSVersionCodename: "bookworm", Maintainer: "Guillem Jover ", Architecture: "amd64", @@ -398,6 +469,7 @@ func TestExtract(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "acl", PackageVersion: "2.3.1-3", + Status: "install ok installed", OSID: "ubuntu", OSVersionCodename: "jammy", OSVersionID: "22.04", @@ -438,6 +510,20 @@ func TestExtract(t *testing.T) { Locations: []string{"testdata/status.d/foo"}, Extractor: dpkg.Name, }, + &extractor.Inventory{ + Name: "bar", + Version: "1.0", + Metadata: &dpkg.Metadata{ + PackageName: "bar", + PackageVersion: "1.0", + Status: "deinstall ok config-files", + OSID: "debian", + OSVersionCodename: "bookworm", + OSVersionID: "12", + }, + Locations: []string{"testdata/status.d/foo"}, + Extractor: dpkg.Name, + }, }, }, } @@ -489,6 +575,7 @@ func TestExtractNonexistentOSRelease(t *testing.T) { Metadata: &dpkg.Metadata{ PackageName: "acl", PackageVersion: "2.3.1-3", + Status: "install ok installed", OSID: "", OSVersionID: "", Maintainer: "Guillem Jover ", diff --git a/extractor/os/dpkg/metadata.go b/extractor/os/dpkg/metadata.go index b891b9b8..da678784 100644 --- a/extractor/os/dpkg/metadata.go +++ b/extractor/os/dpkg/metadata.go @@ -17,6 +17,7 @@ package dpkg // Metadata holds parsing information for an dpkg package. type Metadata struct { PackageName string + Status string SourceName string SourceVersion string PackageVersion string diff --git a/extractor/os/dpkg/testdata/statusfield b/extractor/os/dpkg/testdata/statusfield index 6bd13d60..500a25a0 100644 --- a/extractor/os/dpkg/testdata/statusfield +++ b/extractor/os/dpkg/testdata/statusfield @@ -21,3 +21,6 @@ Version: 1.0 Package: wantinstall_halfinstalled Status: install reinstreq half-installed Version: 1.0 + +Package: wantnostatus +Version: 1.0