Skip to content

feat!(detector): detect oracle and alpine with vuls2 #2157

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jun 19, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 37 additions & 58 deletions detector/detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -318,28 +318,29 @@ func Detect(rs []models.ScanResult, dir string) ([]models.ScanResult, error) {
}

// DetectPkgCves detects OS pkg cves
// pass 2 configs
// pass 3 configs
func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf config.GostConf, vuls2Conf config.Vuls2Conf, logOpts logging.LogOpts, noProgress bool) error {
// Pkg Scan
if isPkgCvesDetactable(r) {
// OVAL, gost(Debian Security Tracker) does not support Package for Raspbian, so skip it.
if r.Family == constant.Raspbian {
r = r.RemoveRaspbianPackFromResult()
}

// Vuls2
if err := vuls2.Detect(r, vuls2Conf, noProgress); err != nil {
return xerrors.Errorf("Failed to detect CVE with Vuls2: %w", err)
}

// OVAL
if err := detectPkgsCvesWithOval(ovalCnf, r, logOpts); err != nil {
return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err)
}
switch r.Family {
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky, constant.Oracle, constant.Alpine:
if err := vuls2.Detect(r, vuls2Conf, noProgress); err != nil {
return xerrors.Errorf("Failed to detect CVE with Vuls2: %w", err)
}
case constant.Fedora, constant.Amazon, constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
if err := detectPkgsCvesWithOval(ovalCnf, r, logOpts); err != nil {
return xerrors.Errorf("Failed to detect CVE with OVAL: %w", err)
}
case constant.Debian, constant.Raspbian, constant.Ubuntu, constant.Windows:
// gost(Debian Security Tracker) does not support Package for Raspbian, so skip it.
if r.Family == constant.Raspbian {
r = r.RemoveRaspbianPackFromResult()
}

// gost
if err := detectPkgsCvesWithGost(gostCnf, r, logOpts); err != nil {
return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
if err := detectPkgsCvesWithGost(gostCnf, r, logOpts); err != nil {
return xerrors.Errorf("Failed to detect CVE with gost: %w", err)
}
default:
return xerrors.Errorf("Unsupported detection methods for %s", r.Family)
}
}

Expand Down Expand Up @@ -377,23 +378,23 @@ func DetectPkgCves(r *models.ScanResult, ovalCnf config.GovalDictConf, gostCnf c
func isPkgCvesDetactable(r *models.ScanResult) bool {
switch r.Family {
case constant.FreeBSD, constant.MacOSX, constant.MacOSXServer, constant.MacOS, constant.MacOSServer, constant.ServerTypePseudo:
logging.Log.Infof("%s type. Skip OVAL and gost detection", r.Family)
logging.Log.Infof("%s type. Skip OVAL, gost and vuls2 detection", r.Family)
return false
case constant.Windows:
return true
default:
if r.ScannedVia == "trivy" {
logging.Log.Infof("r.ScannedVia is trivy. Skip OVAL and gost detection")
logging.Log.Infof("r.ScannedVia is trivy. Skip OVAL, gost and vuls2 detection")
return false
}

if r.Release == "" {
logging.Log.Infof("r.Release is empty. Skip OVAL and gost detection")
logging.Log.Infof("r.Release is empty. Skip OVAL, gost and vuls2 detection")
return false
}

if len(r.Packages)+len(r.SrcPackages) == 0 {
logging.Log.Infof("Number of packages is 0. Skip OVAL and gost detection")
logging.Log.Infof("Number of packages is 0. Skip OVAL, gost and vuls2 detection")
return false
}
return true
Expand Down Expand Up @@ -531,52 +532,40 @@ func fillCertAlerts(cvedetail *cvemodels.CveDetail) (dict models.AlertDict) {
return dict
}

// detectPkgsCvesWithOval fetches OVAL database
func detectPkgsCvesWithOval(cnf config.GovalDictConf, r *models.ScanResult, logOpts logging.LogOpts) error {
client, err := oval.NewOVALClient(r.Family, cnf, logOpts)
if err != nil {
return err
return xerrors.Errorf("Failed to new OVAL client. err: %w", err)
}
defer func() {
if err := client.CloseDB(); err != nil {
logging.Log.Errorf("Failed to close the OVAL DB. err: %+v", err)
}
}()

switch r.Family {
case constant.Debian, constant.Raspbian, constant.Ubuntu:
logging.Log.Infof("Skip OVAL and Scan with gost alone.")
logging.Log.Infof("%s: %d CVEs are detected with OVAL", r.FormatServerName(), 0)
return nil
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
logging.Log.Debugf("Skip OVAL and Scan by Vuls2")
return nil
case constant.Windows, constant.MacOSX, constant.MacOSXServer, constant.MacOS, constant.MacOSServer, constant.FreeBSD, constant.ServerTypePseudo:
return nil
default:
logging.Log.Debugf("Check if oval fetched: %s %s", r.Family, r.Release)
ok, err := client.CheckIfOvalFetched(r.Family, r.Release)
if err != nil {
return err
}
if !ok {
return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/vulsio/goval-dictionary#usage`", r.Family, r.Release)
}
logging.Log.Debugf("Check if oval fetched: %s %s", r.Family, r.Release)
ok, err := client.CheckIfOvalFetched(r.Family, r.Release)
if err != nil {
return xerrors.Errorf("Failed to check if oval fetched: %w", err)
}
if !ok {
return xerrors.Errorf("OVAL entries of %s %s are not found. Fetch OVAL before reporting. For details, see `https://github.com/vulsio/goval-dictionary#usage`", r.Family, r.Release)
}

logging.Log.Debugf("Check if oval fresh: %s %s", r.Family, r.Release)
_, err = client.CheckIfOvalFresh(r.Family, r.Release)
if err != nil {
return err
return xerrors.Errorf("Failed to check if oval fresh: %w", err)
}

logging.Log.Debugf("Fill with oval: %s %s", r.Family, r.Release)
nCVEs, err := client.FillWithOval(r)
if err != nil {
return err
return xerrors.Errorf("Failed to fill with oval: %w", err)
}

logging.Log.Infof("%s: %d CVEs are detected with OVAL", r.FormatServerName(), nCVEs)

return nil
}

Expand All @@ -593,20 +582,10 @@ func detectPkgsCvesWithGost(cnf config.GostConf, r *models.ScanResult, logOpts l

nCVEs, err := client.DetectCVEs(r, true)
if err != nil {
switch r.Family {
case constant.Debian, constant.Raspbian, constant.Ubuntu, constant.Windows:
return xerrors.Errorf("Failed to detect CVEs with gost: %w", err)
default:
return xerrors.Errorf("Failed to detect unfixed CVEs with gost: %w", err)
}
return xerrors.Errorf("Failed to detect CVEs with gost: %w", err)
}

switch r.Family {
case constant.Debian, constant.Raspbian, constant.Ubuntu, constant.Windows:
logging.Log.Infof("%s: %d CVEs are detected with gost", r.FormatServerName(), nCVEs)
default:
logging.Log.Infof("%s: %d unfixed CVEs are detected with gost", r.FormatServerName(), nCVEs)
}
logging.Log.Infof("%s: %d CVEs are detected with gost", r.FormatServerName(), nCVEs)

return nil
}
Expand Down
24 changes: 23 additions & 1 deletion detector/vuls2/vendor.go
Original file line number Diff line number Diff line change
Expand Up @@ -245,6 +245,12 @@ func advisoryReference(e ecosystemTypes.Ecosystem, s sourceTypes.SourceID, da mo
Source: "ROCKY",
RefID: da.AdvisoryID,
}, nil
case ecosystemTypes.EcosystemTypeOracle:
return models.Reference{
Link: fmt.Sprintf("https://linux.oracle.com/errata/%s.html", da.AdvisoryID),
Source: "ORACLE",
RefID: da.AdvisoryID,
}, nil
default:
return models.Reference{}, xerrors.Errorf("unsupported family: %s", et)
}
Expand All @@ -254,6 +260,10 @@ func cveContentSourceLink(ccType models.CveContentType, v vulnerabilityTypes.Vul
switch ccType {
case models.RedHat:
return fmt.Sprintf("https://access.redhat.com/security/cve/%s", v.Content.ID)
case models.Oracle:
return fmt.Sprintf("https://linux.oracle.com/cve/%s.html", v.Content.ID)
case models.Alpine:
return fmt.Sprintf("https://security.alpinelinux.org/vuln/%s", v.Content.ID)
case models.Nvd:
return fmt.Sprintf("https://nvd.nist.gov/vuln/detail/%s", v.Content.ID)
default:
Expand Down Expand Up @@ -343,6 +353,18 @@ func compareSourceID(e ecosystemTypes.Ecosystem, a, b sourceTypes.SourceID) int
}
}
return cmp.Compare(preferenceFn(a), preferenceFn(b))
case ecosystemTypes.EcosystemTypeAlpine:
preferenceFn := func(sourceID sourceTypes.SourceID) int {
switch sourceID {
case sourceTypes.AlpineSecDB:
return 3
case sourceTypes.AlpineOSV:
return 2
default:
return 1
}
}
return cmp.Compare(preferenceFn(a), preferenceFn(b))
default:
return 0
}
Expand Down Expand Up @@ -443,7 +465,7 @@ func toVuls0Confidence(e ecosystemTypes.Ecosystem, s sourceTypes.SourceID) model
DetectionMethod: models.DetectionMethod("EPELMatch"),
SortOrder: 1,
}
case ecosystemTypes.EcosystemTypeRedHat, ecosystemTypes.EcosystemTypeAlma, ecosystemTypes.EcosystemTypeRocky:
case ecosystemTypes.EcosystemTypeRedHat, ecosystemTypes.EcosystemTypeAlma, ecosystemTypes.EcosystemTypeRocky, ecosystemTypes.EcosystemTypeOracle, ecosystemTypes.EcosystemTypeAlpine:
return models.OvalMatch
default:
return models.Confidence{
Expand Down
33 changes: 26 additions & 7 deletions detector/vuls2/vuls2.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,12 @@ import (
"github.com/MaineK00n/vuls2/pkg/version"

"github.com/future-architect/vuls/config"
"github.com/future-architect/vuls/constant"
"github.com/future-architect/vuls/logging"
"github.com/future-architect/vuls/models"
)

// Detect detects vulnerabilities and fills ScanResult
func Detect(r *models.ScanResult, vuls2Conf config.Vuls2Conf, noProgress bool) error {
switch r.Family {
case constant.RedHat, constant.CentOS, constant.Alma, constant.Rocky:
default:
return nil
}

if vuls2Conf.Repository == "" {
vuls2Conf.Repository = DefaultGHCRRepository
}
Expand Down Expand Up @@ -976,6 +969,32 @@ func toReference(ref string) models.Reference {
Source: "ROCKY",
RefID: strings.TrimPrefix(ref, "https://errata.build.resf.org/"),
}
case strings.HasPrefix(ref, "https://linux.oracle.com/"):
switch {
case strings.HasPrefix(ref, "https://linux.oracle.com/cve/"):
return models.Reference{
Link: ref,
Source: "ORACLE",
RefID: strings.TrimPrefix(strings.TrimSuffix(ref, ".html"), "https://linux.oracle.com/cve/"),
}
case strings.HasPrefix(ref, "https://linux.oracle.com/errata/"):
return models.Reference{
Link: ref,
Source: "ORACLE",
RefID: strings.TrimPrefix(strings.TrimSuffix(ref, ".html"), "https://linux.oracle.com/errata/"),
}
default:
return models.Reference{
Link: ref,
Source: "ORACLE",
}
}
case strings.HasPrefix(ref, "https://security.alpinelinux.org/vuln/"):
return models.Reference{
Link: ref,
Source: "ALPINE",
RefID: strings.TrimPrefix(ref, "https://security.alpinelinux.org/vuln/"),
}
default:
return models.Reference{
Link: ref,
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ require (
github.com/hashicorp/go-version v1.7.0
github.com/jesseduffield/gocui v0.3.0
github.com/k0kubun/pp v3.0.1+incompatible
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f
github.com/knqyf263/go-cpe v0.0.0-20230627041855-cb0794d06872
github.com/knqyf263/go-deb-version v0.0.0-20241115132648-6f4aee6ccd23
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f
Expand Down Expand Up @@ -234,6 +233,7 @@ require (
github.com/jtolds/gls v4.20.0+incompatible // indirect
github.com/kevinburke/ssh_config v1.2.0 // indirect
github.com/klauspost/compress v1.18.0 // indirect
github.com/knqyf263/go-apk-version v0.0.0-20200609155635-041fdbb8563f // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect
github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect
Expand Down
7 changes: 6 additions & 1 deletion gost/gost.go
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,13 @@ func NewGostClient(cnf config.GostConf, family string, o logging.LogOpts) (Clien
return Ubuntu{base}, nil
case constant.Windows:
return Microsoft{base}, nil
default:
case constant.ServerTypePseudo:
return Pseudo{base}, nil
default:
if family == "" {
return nil, xerrors.New("Probably an error occurred during scanning. Check the error message")
}
return nil, xerrors.Errorf("Gost for %s is not implemented yet", family)
}
}

Expand Down
3 changes: 0 additions & 3 deletions gost/gost_test.go

This file was deleted.

2 changes: 1 addition & 1 deletion integration
9 changes: 9 additions & 0 deletions models/cvecontents.go
Original file line number Diff line number Diff line change
Expand Up @@ -336,6 +336,8 @@ func NewCveContentType(name string) CveContentType {
return UbuntuAPI
case constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
return SUSE
case "alpine":
return Alpine
case "microsoft":
return Microsoft
case "wordpress":
Expand Down Expand Up @@ -428,6 +430,8 @@ func GetCveContentTypes(family string) []CveContentType {
return []CveContentType{Ubuntu, UbuntuAPI}
case constant.OpenSUSE, constant.OpenSUSELeap, constant.SUSEEnterpriseServer, constant.SUSEEnterpriseDesktop:
return []CveContentType{SUSE}
case constant.Alpine:
return []CveContentType{Alpine}
case constant.Windows:
return []CveContentType{Microsoft}
case string(Trivy):
Expand Down Expand Up @@ -492,6 +496,9 @@ const (
// SUSE is SUSE Linux
SUSE CveContentType = "suse"

// Alpine is Alpine Linux
Alpine CveContentType = "alpine"

// Microsoft is Microsoft
Microsoft CveContentType = "microsoft"

Expand Down Expand Up @@ -614,6 +621,8 @@ var AllCveContetTypes = CveContentTypes{
Amazon,
Fedora,
SUSE,
Alpine,
Microsoft,
WpScan,
Trivy,
TrivyNVD,
Expand Down
Loading
Loading