diff --git a/agent/internal/packages/packages.go b/agent/internal/packages/packages.go index fafcacc..a503223 100644 --- a/agent/internal/packages/packages.go +++ b/agent/internal/packages/packages.go @@ -33,7 +33,7 @@ func Collect() (OSRelease, []Package, error) { switch { case have("dpkg-query"): out, err := run(ctx, "dpkg-query", "-W", "-f", - `${Package}\t${Version}\t${Architecture}\t${source:Package}\n`) + `${Package}\t${Version}\t${Architecture}\t${source:Package}\t${db:Status-Status}\n`) if err != nil { return osrel, nil, err } diff --git a/agent/internal/packages/parse.go b/agent/internal/packages/parse.go index 41adf04..2365332 100644 --- a/agent/internal/packages/parse.go +++ b/agent/internal/packages/parse.go @@ -20,12 +20,20 @@ type Package struct { } // ParseDpkg reads tab-separated output of -// dpkg-query -W -f '${Package}\t${Version}\t${Architecture}\t${source:Package}\n' +// dpkg-query -W -f '${Package}\t${Version}\t${Architecture}\t${source:Package}\t${db:Status-Status}\n' // // SourceName is why the fourth column is requested at all: Debian and Ubuntu // advisories are keyed on the SOURCE package, so one CVE against "openssl" // covers the binaries libssl3, openssl and libssl-dev. Matching on binary name // alone finds one of the three. +// +// The fifth column is why "rc" packages do not appear. dpkg-query -W lists +// every package dpkg knows about, including ones removed with their config +// files left behind — a host that has upgraded its kernel a dozen times reports +// a dozen old linux-modules versions that are not on disk, and the oldest of +// them sorts first and reads as the installed version. Only "installed" is +// installed. An empty status means dpkg did not understand the field, in which +// case the line is kept rather than the whole inventory silently vanishing. func ParseDpkg(out string) []Package { var pkgs []Package for _, line := range strings.Split(out, "\n") { @@ -36,6 +44,11 @@ func ParseDpkg(out string) []Package { if len(f) < 3 { continue } + if len(f) > 4 { + if s := strings.TrimSpace(f[4]); s != "" && s != "installed" { + continue + } + } p := Package{Name: f[0], Version: f[1], Arch: f[2]} if len(f) > 3 && f[3] != "" { p.SourceName = f[3]