Skip to content
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

fix(oracle): split version flavors #452

Draft
wants to merge 6 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ require (
github.com/briandowns/spinner v1.23.0
github.com/fatih/color v1.10.0 // indirect
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f
github.com/masahiro331/go-mvn-version v0.0.0-20210429150710-d3157d602a08
github.com/pandatix/go-cvss v0.6.2
github.com/samber/lo v1.47.0
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d h1:X4cedH4
github.com/knqyf263/go-deb-version v0.0.0-20190517075300-09fca494f03d/go.mod h1:o8sgWoz3JADecfc/cTYD92/Et1yMqMy0utV1z+VaZao=
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936 h1:HDjRqotkViMNcGMGicb7cgxklx8OwnjtCBmyWEqrRvM=
github.com/knqyf263/go-rpm-version v0.0.0-20170716094938-74609b86c936/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f h1:xt29M2T6STgldg+WEP51gGePQCsQvklmP2eIhPIBK3g=
github.com/knqyf263/go-rpm-version v0.0.0-20240918084003-2afd7dc6a38f/go.mod h1:i4sF0l1fFnY1aiw08QQSwVAFxHEm311Me3WsU/X7nL0=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
Expand Down
106 changes: 96 additions & 10 deletions pkg/vulnsrc/oracle-oval/oracle-oval.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ import (
"strings"

version "github.com/knqyf263/go-rpm-version"
"github.com/samber/lo"
bolt "go.etcd.io/bbolt"
"golang.org/x/exp/maps"
"golang.org/x/xerrors"

"github.com/aquasecurity/trivy-db/pkg/db"
"github.com/aquasecurity/trivy-db/pkg/types"
"github.com/aquasecurity/trivy-db/pkg/utils"
ustrings "github.com/aquasecurity/trivy-db/pkg/utils/strings"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
)

Expand All @@ -37,7 +38,7 @@ type PutInput struct {
VulnID string // CVE-ID or ELSA-ID
Vuln types.VulnerabilityDetail // vulnerability detail such as CVSS and description
Advisories map[AffectedPackage]types.Advisory // pkg => advisory
OVAL OracleOVAL // for extensibility, not used in trivy-db
OVALs []OracleOVAL // for extensibility, not used in trivy-db
}

type DB interface {
Expand Down Expand Up @@ -111,6 +112,7 @@ func (vs *VulnSrc) put(ovals []OracleOVAL) error {
}

func (vs *VulnSrc) commit(tx *bolt.Tx, ovals []OracleOVAL) error {
putInputs := make(map[string]PutInput)
for _, oval := range ovals {
elsaID := strings.Split(oval.Title, ":")[0]

Expand Down Expand Up @@ -138,8 +140,13 @@ func (vs *VulnSrc) commit(tx *bolt.Tx, ovals []OracleOVAL) error {
return xerrors.Errorf("failed to put data source: %w", err)
}

// Clean affectedPkg.Package.FixedVersion to find same packages,
// when we merge fixed versions from multiple ELSA files
fixedVersion := affectedPkg.Package.FixedVersion
affectedPkg.Package.FixedVersion = ""

advisories[affectedPkg] = types.Advisory{
FixedVersion: affectedPkg.Package.FixedVersion,
PatchedVersions: []string{fixedVersion},
}
}

Expand All @@ -156,21 +163,96 @@ func (vs *VulnSrc) commit(tx *bolt.Tx, ovals []OracleOVAL) error {
Severity: severityFromThreat(oval.Severity),
}

err := vs.Put(tx, PutInput{
input := PutInput{
VulnID: vulnID,
Vuln: vuln,
Advisories: advisories,
OVAL: oval,
})
if err != nil {
return xerrors.Errorf("db put error: %w", err)
Advisories: maps.Clone(advisories),
OVALs: []OracleOVAL{oval},
}
if savedInput, ok := putInputs[input.VulnID]; ok {
input.OVALs = append(input.OVALs, savedInput.OVALs...)

for newPkg, newAdv := range input.Advisories {
if savedPkgAdv, pkgFound := savedInput.Advisories[newPkg]; pkgFound {
// Merge patchedVersions.
// We will remove duplicates later.
newAdv.PatchedVersions = append(savedPkgAdv.PatchedVersions, newAdv.PatchedVersions...)
}
savedInput.Advisories[newPkg] = newAdv
}
input.Advisories = savedInput.Advisories
}
putInputs[input.VulnID] = input
}
}

for _, input := range putInputs {
for pkg, adv := range input.Advisories {
// Remove duplicates and multiple version for one flavor.
// Keep only the normal version in adv.FixedVersion for backward compatibility.
adv.FixedVersion, adv.PatchedVersions = patchedVersions(adv.PatchedVersions)
input.Advisories[pkg] = adv
}

err := vs.Put(tx, input)
if err != nil {
return xerrors.Errorf("db put error: %w", err)
}
}

return nil
}

type PkgFlavor string

const (
NormalPackageFlavor PkgFlavor = "normal"
FipsPackageFlavor PkgFlavor = "fips"
KsplicePackageFlavor PkgFlavor = "ksplice"
)

// patchedVersions removes duplicates and returns normal flavor + only one version for each flavor.
func patchedVersions(vers []string) (string, []string) {
vers = lo.Uniq(vers)

patchedVers := make(map[PkgFlavor]string)
for _, ver := range vers {
flavor := PackageFlavor(ver)
if savedVer, ok := patchedVers[flavor]; ok {
v := version.NewVersion(ver)
sv := version.NewVersion(savedVer)
if v.LessThan(sv) {
ver = savedVer
}
}
patchedVers[flavor] = ver
}

versions := lo.Values(patchedVers)
slices.Sort(versions)

return patchedVers[NormalPackageFlavor], versions
}

// PackageFlavor determinants the package "flavor" based on its version string
// - normal
// - FIPS validated
// - ksplice userspace
func PackageFlavor(version string) PkgFlavor {
version = strings.ToLower(version)
if strings.HasSuffix(version, "_fips") {
return FipsPackageFlavor
}

subs := strings.Split(version, ".")
for _, s := range subs {
if strings.HasPrefix(s, "ksplice") {
return KsplicePackageFlavor
}
}
return NormalPackageFlavor
}

func (o *Oracle) Put(tx *bolt.Tx, input PutInput) error {
if err := o.PutVulnerabilityDetail(tx, input.VulnID, source.ID, input.Vuln); err != nil {
return xerrors.Errorf("failed to save Oracle Linux OVAL vulnerability: %w", err)
Expand Down Expand Up @@ -234,7 +316,11 @@ func referencesFromContains(sources []string, matches []string) []string {
}
}
}
return ustrings.Unique(references)

references = lo.Uniq(references)
slices.Sort(references)

return references
}

func severityFromThreat(sev string) types.Severity {
Expand Down
Loading