blob: bf8d7a1789e2f5047d92cc0c34808be7ecabba46 [file] [edit]
// Copyright 2017 Google Inc. All Rights Reserved.
//
// 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 packages
import (
"context"
"errors"
"fmt"
"slices"
"strings"
"github.com/GoogleCloudPlatform/osconfig/agentconfig"
"github.com/GoogleCloudPlatform/osconfig/clog"
"github.com/GoogleCloudPlatform/osconfig/osinfo"
"github.com/package-url/packageurl-go"
)
var (
scalibrLinuxEnabled = agentconfig.ScalibrLinuxEnabled
extendedInventoryEnabled = agentconfig.ExtendedInventoryEnabled
extendedInventoryExtractorsAllowed = agentconfig.ExtendedInventoryExtractorsAllowed
)
// GetPackageUpdates gets all available package updates from any known
// installed package manager.
func (p defaultUpdatesProvider) getPackageUpdates(ctx context.Context) (Packages, error) {
var err error
oi, err := p.osinfoProvider.GetOSInfo(ctx)
if err != nil {
clog.Errorf(ctx, "osinfo.Get() error: %v", err)
}
pkgs, errs := getPackageUpdates(ctx, oi)
if len(errs) != 0 {
err = errors.New(strings.Join(errs, "\n"))
}
return pkgs, err
}
func getPackageUpdates(ctx context.Context, oi osinfo.OSInfo) (Packages, []string) {
pkgs := Packages{}
shortname := oi.ShortName
var errs []string
if AptExists {
apt, err := AptUpdates(ctx, AptGetUpgradeType(AptGetFullUpgrade), AptGetUpgradeShowNew(false))
if err != nil {
msg := fmt.Sprintf("error getting apt updates: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
errs = append(errs, msg)
} else {
apt = enrichDebPkgInfoWithPurl(apt, shortname, oi.Version)
pkgs.Apt = apt
}
}
if YumExists {
yum, err := YumUpdates(ctx)
if err != nil {
msg := fmt.Sprintf("error getting yum updates: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
errs = append(errs, msg)
} else {
yum = enrichRpmPkgInfoWithPurl(yum, shortname, oi.Version)
pkgs.Yum = yum
}
}
if ZypperExists {
zypper, err := ZypperUpdates(ctx)
if err != nil {
msg := fmt.Sprintf("error getting zypper updates: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
errs = append(errs, msg)
} else {
zypper = enrichRpmPkgInfoWithPurl(zypper, shortname, oi.Version)
pkgs.Zypper = zypper
}
zypperPatches, err := ZypperPatches(ctx)
if err != nil {
msg := fmt.Sprintf("error getting zypper available patches: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
errs = append(errs, msg)
} else {
zypperPatches = enrichZypperPatchWithPurl(zypperPatches, shortname)
pkgs.ZypperPatches = zypperPatches
}
}
if GemExists {
gem, err := GemUpdates(ctx)
if err != nil {
msg := fmt.Sprintf("error getting gem updates: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
} else {
gem = enrichGemPkgInfoWithPurl(gem)
pkgs.Gem = gem
}
}
if PipExists {
pip, err := PipUpdates(ctx)
if err != nil {
msg := fmt.Sprintf("error getting pip updates: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
} else {
pip = enrichPipPkgInfoWithPurl(pip)
pkgs.Pip = pip
}
}
return pkgs, errs
}
// GetInstalledPackages gets all installed packages from any known installed
// package manager.
func (p defaultInstalledPackagesProvider) getInstalledPackages(ctx context.Context) (Packages, error) {
var err error
oi, err := p.osinfoProvider.GetOSInfo(ctx)
if err != nil {
clog.Errorf(ctx, "osinfo.Get() error: %v", err)
}
pkgs, errs := getInstalledPackages(ctx, oi)
if len(errs) != 0 {
err = errors.New(strings.Join(errs, "\n"))
}
return pkgs, err
}
func getInstalledPackages(ctx context.Context, oi osinfo.OSInfo) (Packages, []string) {
pkgs := Packages{}
shortname := oi.ShortName
var errs []string
if RPMQueryExists {
rpm, err := InstalledRPMPackages(ctx)
if err != nil {
msg := fmt.Sprintf("error listing installed rpm packages: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
errs = append(errs, msg)
} else {
rpm = enrichRpmPkgInfoWithPurl(rpm, shortname, oi.Version)
pkgs.Rpm = rpm
}
}
if ZypperExists {
zypperPatches, err := ZypperInstalledPatches(ctx)
if err != nil {
msg := fmt.Sprintf("error getting zypper installed patches: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
errs = append(errs, msg)
} else {
zypperPatches = enrichZypperPatchWithPurl(zypperPatches, shortname)
pkgs.ZypperPatches = zypperPatches
}
}
if DpkgQueryExists {
deb, err := InstalledDebPackages(ctx)
if err != nil {
msg := fmt.Sprintf("error listing installed deb packages: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
errs = append(errs, msg)
} else {
deb = enrichDebPkgInfoWithPurl(deb, shortname, oi.Version)
pkgs.Deb = deb
}
}
if COSPkgInfoExists {
cos, err := InstalledCOSPackages()
if err != nil {
msg := fmt.Sprintf("error listing installed COS packages: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
errs = append(errs, msg)
} else {
cos = enrichCosPkgInfoWithPurl(cos, shortname, oi.Version)
pkgs.COS = cos
}
}
if GemExists {
gem, err := InstalledGemPackages(ctx)
if err != nil {
msg := fmt.Sprintf("error listing installed gem packages: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
} else {
gem = enrichGemPkgInfoWithPurl(gem)
pkgs.Gem = gem
}
}
if PipExists {
pip, err := InstalledPipPackages(ctx)
if err != nil {
msg := fmt.Sprintf("error listing installed pip packages: %v", err)
clog.Debugf(ctx, "Error: %s", msg)
} else {
pip = enrichPipPkgInfoWithPurl(pip)
pkgs.Pip = pip
}
}
return pkgs, errs
}
func enrichDebPkgInfoWithPurl(pkgs []*PkgInfo, shortname string, version string) []*PkgInfo {
for i, pkg := range pkgs {
qualifiersMap := map[string]string{
"source": pkg.Source.Name,
"arch": pkg.Arch,
"distro": version, // For Deb, accepted distro format is version (eg. distro=9.1)
}
pkgs[i].Purl = packageurl.NewPackageURL(pkg.Type, shortname, pkg.Name, pkg.Version, packageurl.QualifiersFromMap(qualifiersMap), "").ToString()
}
return pkgs
}
func enrichRpmPkgInfoWithPurl(pkgs []*PkgInfo, shortname string, version string) []*PkgInfo {
for i, pkg := range pkgs {
qualifiersMap := map[string]string{
"arch": pkg.Arch,
"distro": version, // For RPM, accepted distro format is version (eg. distro=9.1)
}
pkgs[i].Purl = packageurl.NewPackageURL(pkg.Type, shortname, pkg.Name, pkg.Version, packageurl.QualifiersFromMap(qualifiersMap), "").ToString()
}
return pkgs
}
func enrichCosPkgInfoWithPurl(pkgs []*PkgInfo, shortname string, version string) []*PkgInfo {
for i, pkg := range pkgs {
qualifiersMap := map[string]string{
"arch": pkg.Arch,
"distro": fmt.Sprintf("%s-%s", shortname, version), // For COS, required distro format is OS and version (eg. distro=cos-101)
}
pkgs[i].Purl = packageurl.NewPackageURL(pkg.Type, shortname, pkg.Name, pkg.Version, packageurl.QualifiersFromMap(qualifiersMap), "").ToString()
}
return pkgs
}
func enrichGemPkgInfoWithPurl(pkgs []*PkgInfo) []*PkgInfo {
for i, pkg := range pkgs {
pkgs[i].Purl = packageurl.NewPackageURL(pkg.Type, "", pkg.Name, pkg.Version, packageurl.Qualifiers{}, "").ToString()
}
return pkgs
}
func enrichPipPkgInfoWithPurl(pkgs []*PkgInfo) []*PkgInfo {
for i, pkg := range pkgs {
pkgs[i].Purl = packageurl.NewPackageURL(pkg.Type, "", pkg.Name, pkg.Version, packageurl.Qualifiers{}, "").ToString()
}
return pkgs
}
func enrichZypperPatchWithPurl(pkgs []*ZypperPatch, shortname string) []*ZypperPatch {
for i, pkg := range pkgs {
qualifiers := packageurl.Qualifiers{packageurl.Qualifier{Key: "severity", Value: pkg.Severity}}
pkgs[i].Purl = packageurl.NewPackageURL(packageurl.TypeGeneric, shortname, pkg.Name, "", qualifiers, "").ToString()
}
return pkgs
}
// NewInstalledPackagesProvider makes provider that uses osv-scalibr as its implementation if enabled by config, otherwise falls back to default legacy implementation.
func NewInstalledPackagesProvider(osinfoProvider osinfo.Provider) InstalledPackagesProvider {
if scalibrLinuxEnabled() {
extractors := []string{
"os/cos",
"os/dpkg",
"os/rpm",
}
if extendedInventoryEnabled() {
for _, ext := range extendedInventoryExtractorsAllowed() {
if !slices.Contains(extractors, ext) {
extractors = append(extractors, ext)
}
}
}
return &scalibrInstalledPackagesProvider{
extractors: extractors,
osinfoProvider: osinfoProvider,
}
}
return defaultInstalledPackagesProvider{
osinfoProvider: osinfoProvider,
}
}