Skip to content

Commit

Permalink
feat: add ALT Linux support
Browse files Browse the repository at this point in the history
Co-Authored-By: stefan <stefan_paksa@icloud.com>
  • Loading branch information
fl0pp5 and ipaqsa committed Apr 1, 2024
1 parent 5f69937 commit c79884b
Show file tree
Hide file tree
Showing 10 changed files with 256 additions and 10 deletions.
8 changes: 5 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -96,14 +96,14 @@ require (
github.com/spf13/cobra v1.8.0
github.com/spf13/pflag v1.0.5
github.com/spf13/viper v1.18.2
github.com/stretchr/testify v1.8.4
github.com/stretchr/testify v1.9.0
github.com/testcontainers/testcontainers-go v0.28.0
github.com/testcontainers/testcontainers-go/modules/localstack v0.26.0
github.com/tetratelabs/wazero v1.7.0
github.com/twitchtv/twirp v8.1.2+incompatible
github.com/xeipuuv/gojsonschema v1.2.0
github.com/xlab/treeprint v1.2.0
go.etcd.io/bbolt v1.3.8
go.etcd.io/bbolt v1.3.9
go.uber.org/zap v1.27.0
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa
golang.org/x/mod v0.15.0
Expand Down Expand Up @@ -366,7 +366,7 @@ require (
github.com/skeema/knownhosts v1.2.1 // indirect
github.com/sourcegraph/conc v0.3.0 // indirect
github.com/spf13/afero v1.11.0 // indirect
github.com/stretchr/objx v0.5.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/tchap/go-patricia/v2 v2.3.1 // indirect
github.com/ulikunitz/xz v0.5.11 // indirect
Expand Down Expand Up @@ -431,3 +431,5 @@ require (
// testcontainers-go has a bug with versions v0.25.0 and v0.26.0
// ref: https://github.com/testcontainers/testcontainers-go/issues/1782
replace github.com/testcontainers/testcontainers-go => github.com/testcontainers/testcontainers-go v0.23.0

replace github.com/aquasecurity/trivy-db => github.com/altlinux/trivy-db v0.0.0-20240401141737-336f6ffaab21
14 changes: 8 additions & 6 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@ github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a h1:HbKu58rmZp
github.com/alicebob/gopher-json v0.0.0-20200520072559-a9ecdc9d1d3a/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
github.com/alicebob/miniredis/v2 v2.31.1 h1:7XAt0uUg3DtwEKW5ZAGa+K7FZV2DdKQo5K/6TTnfX8Y=
github.com/alicebob/miniredis/v2 v2.31.1/go.mod h1:UB/T2Uztp7MlFSDakaX1sTXUv5CASoprx0wulRT6HBg=
github.com/altlinux/trivy-db v0.0.0-20240401141737-336f6ffaab21 h1:ogBUREezeqGdM56cqJAyXPhXaTE4ZQX81k+du9VM150=
github.com/altlinux/trivy-db v0.0.0-20240401141737-336f6ffaab21/go.mod h1:sECIuPk1bXaqGDdoRVRfJCNNrPwIrutIFrfoVy6AVWE=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc=
github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
Expand Down Expand Up @@ -345,8 +347,6 @@ github.com/aquasecurity/tml v0.6.1 h1:y2ZlGSfrhnn7t4ZJ/0rotuH+v5Jgv6BDDO5jB6A9gw
github.com/aquasecurity/tml v0.6.1/go.mod h1:OnYMWY5lvI9ejU7yH9LCberWaaTBW7hBFsITiIMY2yY=
github.com/aquasecurity/trivy-aws v0.8.0 h1:4ij8MiZ2sJUH+vWpSeoGVhPr109ZBcNp7LNLfPuv5Cw=
github.com/aquasecurity/trivy-aws v0.8.0/go.mod h1:Pb9xqOuTKMHVgjsnjvudjqZh3nmzdFqFVfRkXnoIZBM=
github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d h1:fjI9mkoTUAkbGqpzt9nJsO24RAdfG+ZSiLFj0G2jO8c=
github.com/aquasecurity/trivy-db v0.0.0-20231005141211-4fc651f7ac8d/go.mod h1:cj9/QmD9N3OZnKQMp+/DvdV+ym3HyIkd4e+F0ZM3ZGs=
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48 h1:JVgBIuIYbwG+ekC5lUHUpGJboPYiCcxiz06RCtz8neI=
github.com/aquasecurity/trivy-java-db v0.0.0-20240109071736-184bd7481d48/go.mod h1:Ldya37FLi0e/5Cjq2T5Bty7cFkzUDwTcPeQua+2M8i8=
github.com/aquasecurity/trivy-kubernetes v0.6.3 h1:Hmo0pefXRsyVYsii62WUQyt3xMHjm37ipPESeWM/LNA=
Expand Down Expand Up @@ -1599,8 +1599,9 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
Expand All @@ -1612,8 +1613,9 @@ github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
Expand Down Expand Up @@ -1699,8 +1701,8 @@ github.com/zclconf/go-cty-yaml v1.0.3/go.mod h1:9YLUH4g7lOhVWqUbctnVlZ5KLpg7JApr
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.8 h1:xs88BrvEv273UsB79e0hcVrlUWmS0a8upikMFhSyAtA=
go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw=
go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI=
go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE=
go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489/go.mod h1:yVHk9ub3CSBatqGNg7GRmsnfLWtoW60w4eDYfh7vHDg=
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g=
Expand Down
167 changes: 167 additions & 0 deletions pkg/detector/ospkg/alt/alt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
package alt

import (
"context"
"sort"
"strings"
"time"

dbTypes "github.com/aquasecurity/trivy-db/pkg/types"
ustrings "github.com/aquasecurity/trivy-db/pkg/utils/strings"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/alt"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
osver "github.com/aquasecurity/trivy/pkg/detector/ospkg/version"
ftypes "github.com/aquasecurity/trivy/pkg/fanal/types"
"github.com/aquasecurity/trivy/pkg/log"
"github.com/aquasecurity/trivy/pkg/scanner/utils"
"github.com/aquasecurity/trivy/pkg/types"
"github.com/cheggaaa/pb/v3"
version "github.com/knqyf263/go-rpm-version"
"golang.org/x/exp/maps"
"golang.org/x/exp/slices"
"golang.org/x/xerrors"
"k8s.io/utils/clock"
)

var (
eolDates = map[string]time.Time{
"p9": time.Date(2023, 12, 31, 23, 59, 59, 0, time.UTC),
"p10": time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC),
"c10f1": time.Date(2024, 6, 30, 23, 59, 59, 0, time.UTC),
}
)

type options struct {
clock clock.Clock
}

type option func(*options)

func WithClock(clock clock.Clock) option {
return func(opts *options) {
opts.clock = clock
}
}

// Scanner implements the ALT scanner with ALT` vuln source
type Scanner struct {
vs alt.VulnSrc
*options
}

// NewScanner is the factory method for Scanner
func NewScanner(opts ...option) *Scanner {
o := &options{
clock: clock.RealClock{},
}

for _, opt := range opts {
opt(o)
}
return &Scanner{
vs: alt.NewVulnSrc(),
options: o,
}
}

// IsSupportedVersion checks the OSFamily can be scanned using ALT scanner
func (s *Scanner) IsSupportedVersion(ctx context.Context, osFamily ftypes.OSType, osVer string) bool {
return osver.Supported(ctx, eolDates, osFamily, osVer)
}

func (s *Scanner) Detect(cpe string, _ *ftypes.Repository, pkgs []ftypes.Package) ([]types.DetectedVulnerability, error) {
log.Logger.Info("Detecting ALT vulnerabilities...")
log.Logger.Debugf("ALT: os version: %s", fromCPE(cpe))
log.Logger.Debugf("ALT: the number of packages: %d", len(pkgs))

var vulns []types.DetectedVulnerability
p := pb.New(len(pkgs))
p.Start()
for _, pkg := range pkgs {
detectedVulns, err := s.detect(cpe, pkg)
if err != nil {
return nil, xerrors.Errorf("ALT vulnerability detection error: %w", err)
}
vulns = append(vulns, detectedVulns...)
p.Increment()
}
p.Finish()
return vulns, nil
}

func (s *Scanner) detect(cpe string, pkg ftypes.Package) ([]types.DetectedVulnerability, error) {
advisories, err := s.vs.Get(pkg.Name, cpe)
if err != nil {
return nil, xerrors.Errorf("failed to get ALT advisories: %w", err)
}

installed := utils.FormatVersion(pkg)
installedVersion := version.NewVersion(installed)

uniqVulns := map[string]types.DetectedVulnerability{}
for _, adv := range advisories {
if len(adv.Arches) != 0 && pkg.Arch != "noarch" {
if !slices.Contains(adv.Arches, pkg.Arch) {
continue
}
}
vulnID := adv.VulnerabilityID
vuln := types.DetectedVulnerability{
VulnerabilityID: vulnID,
PkgID: pkg.ID,
PkgName: pkg.Name,
InstalledVersion: utils.FormatVersion(pkg),
PkgIdentifier: pkg.Identifier,
Layer: pkg.Layer,
SeveritySource: vulnerability.ALT,
Vulnerability: dbTypes.Vulnerability{
Severity: adv.Severity.String(),
},
Custom: adv.Custom,
}

if adv.FixedVersion == "" {
if _, ok := uniqVulns[vulnID]; !ok {
uniqVulns[vulnID] = vuln
}
continue
}

fixedVersion := version.NewVersion(adv.FixedVersion)
if installedVersion.LessThan(fixedVersion) {
vuln.VendorIDs = adv.VendorIDs
vuln.FixedVersion = fixedVersion.String()

if v, ok := uniqVulns[vulnID]; ok {
v.VendorIDs = ustrings.Unique(append(v.VendorIDs, vuln.VendorIDs...))

if version.NewVersion(v.FixedVersion).LessThan(fixedVersion) {
v.FixedVersion = vuln.FixedVersion
}
uniqVulns[vulnID] = v
} else {
uniqVulns[vulnID] = vuln
}
}
}

vulns := maps.Values(uniqVulns)
sort.Slice(vulns, func(i, j int) bool {
return vulns[i].VulnerabilityID < vulns[j].VulnerabilityID
})

return vulns, nil
}

func fromCPE(cpe string) string {
if strings.Contains(cpe, "sp") && strings.Contains(cpe, "10") {
return "c10f1"
}
if !strings.Contains(cpe, "sp") && strings.Contains(cpe, "10") {
return "p10"
}
if !strings.Contains(cpe, "sp") && strings.Contains(cpe, "9") {
return "p9"
}
return "undefined"
}
2 changes: 2 additions & 0 deletions pkg/detector/ospkg/detect.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (

"github.com/aquasecurity/trivy/pkg/detector/ospkg/alma"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/alpine"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/alt"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/amazon"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/chainguard"
"github.com/aquasecurity/trivy/pkg/detector/ospkg/debian"
Expand All @@ -33,6 +34,7 @@ var (
ftypes.Alpine: alpine.NewScanner(),
ftypes.Alma: alma.NewScanner(),
ftypes.Amazon: amazon.NewScanner(),
ftypes.ALT: alt.NewScanner(),
ftypes.CBLMariner: mariner.NewScanner(),
ftypes.Debian: debian.NewScanner(),
ftypes.Ubuntu: ubuntu.NewScanner(),
Expand Down
1 change: 1 addition & 0 deletions pkg/fanal/analyzer/all/import.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import (
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/language/swift/swift"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/licensing"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alpine"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/alt"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/amazonlinux"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/debian"
_ "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os/mariner"
Expand Down
1 change: 1 addition & 0 deletions pkg/fanal/analyzer/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ const (
TypeOSRelease Type = "os-release"
TypeAlpine Type = "alpine"
TypeAmazon Type = "amazon"
TypeALT Type = "alt"
TypeCBLMariner Type = "cbl-mariner"
TypeDebian Type = "debian"
TypePhoton Type = "photon"
Expand Down
66 changes: 66 additions & 0 deletions pkg/fanal/analyzer/os/alt/alt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package alt

import (
"bufio"
"context"
"os"
"strings"

"github.com/aquasecurity/trivy/pkg/fanal/analyzer"
fos "github.com/aquasecurity/trivy/pkg/fanal/analyzer/os"
"github.com/aquasecurity/trivy/pkg/fanal/types"
"golang.org/x/exp/slices"
"golang.org/x/xerrors"
)

func init() {
analyzer.RegisterAnalyzer(&altOSAnalyzer{})
}

const altAnalyzerVersion = 1

var requiredFiles = []string{"etc/os-release"}

type altOSAnalyzer struct{}

func (a altOSAnalyzer) Analyze(_ context.Context, input analyzer.AnalysisInput) (*analyzer.AnalysisResult, error) {
scanner := bufio.NewScanner(input.Content)
var cpe string
for scanner.Scan() {
line := scanner.Text()
ss := strings.SplitN(line, "=", 2)
if len(ss) != 2 {
continue
}
key, value := strings.TrimSpace(ss[0]), strings.TrimSpace(ss[1])

switch key {
case "ID":
id := strings.Trim(value, `"'`)
if !strings.Contains(id, "altlinux") {
return nil, nil
}
continue
case "CPE_NAME":
cpe = strings.Trim(value, `"'`)
default:
continue
}
return &analyzer.AnalysisResult{
OS: types.OS{Family: types.ALT, Name: cpe},
}, nil
}
return nil, xerrors.Errorf("alt: %w", fos.AnalyzeOSError)
}

func (a altOSAnalyzer) Required(filePath string, _ os.FileInfo) bool {
return slices.Contains(requiredFiles, filePath)
}

func (a altOSAnalyzer) Type() analyzer.Type {
return analyzer.TypeALT
}

func (a altOSAnalyzer) Version() int {
return altAnalyzerVersion
}
1 change: 1 addition & 0 deletions pkg/fanal/types/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const (
Alma OSType = "alma"
Alpine OSType = "alpine"
Amazon OSType = "amazon"
ALT OSType = "alt"
CBLMariner OSType = "cbl-mariner"
CentOS OSType = "centos"
Chainguard OSType = "chainguard"
Expand Down
2 changes: 1 addition & 1 deletion pkg/purl/purl.go
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@ func purlType(t ftypes.TargetType) string {
return packageurl.TypeDebian
case ftypes.RedHat, ftypes.CentOS, ftypes.Rocky, ftypes.Alma,
ftypes.Amazon, ftypes.Fedora, ftypes.Oracle, ftypes.OpenSUSE,
ftypes.OpenSUSELeap, ftypes.OpenSUSETumbleweed, ftypes.SLES, ftypes.Photon:
ftypes.OpenSUSELeap, ftypes.OpenSUSETumbleweed, ftypes.SLES, ftypes.Photon, ftypes.ALT:
return packageurl.TypeRPM
case TypeOCI:
return packageurl.TypeOCI
Expand Down
4 changes: 4 additions & 0 deletions pkg/vulnerability/vulnerability.go
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,10 @@ func (c Client) getPrimaryURL(vulnID string, refs []string, source dbTypes.Sourc
return "https://github.com/advisories/" + vulnID
case strings.HasPrefix(vulnID, "TEMP-"):
return "https://security-tracker.debian.org/tracker/" + vulnID
case strings.HasPrefix(vulnID, "ALT-"):
return "https://errata.altlinux.org/" + vulnID
case strings.HasPrefix(vulnID, "BDU"):
return "https://bdu.fstec.ru/vul/" + strings.Split(vulnID, ":")[1]
}

prefixes := primaryURLPrefixes[source]
Expand Down

0 comments on commit c79884b

Please sign in to comment.