-
Notifications
You must be signed in to change notification settings - Fork 142
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(rocky): support Rocky Linux (#154)
Co-authored-by: knqyf263 <[email protected]>
- Loading branch information
Showing
12 changed files
with
469 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,171 @@ | ||
package rocky | ||
|
||
import ( | ||
"encoding/json" | ||
"fmt" | ||
"io" | ||
"log" | ||
"path/filepath" | ||
"strings" | ||
|
||
bolt "go.etcd.io/bbolt" | ||
"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" | ||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" | ||
) | ||
|
||
const ( | ||
rockyDir = "rocky" | ||
) | ||
|
||
var ( | ||
platformFormat = "rocky %s" | ||
targetRepos = []string{"BaseOS", "AppStream", "extras"} | ||
targetArches = []string{"x86_64"} | ||
) | ||
|
||
type VulnSrc struct { | ||
dbc db.Operation | ||
} | ||
|
||
func NewVulnSrc() VulnSrc { | ||
return VulnSrc{ | ||
dbc: db.Config{}, | ||
} | ||
} | ||
|
||
func (vs VulnSrc) Name() string { | ||
return vulnerability.Rocky | ||
} | ||
|
||
func (vs VulnSrc) Update(dir string) error { | ||
rootDir := filepath.Join(dir, "vuln-list", rockyDir) | ||
errata := map[string][]RLSA{} | ||
err := utils.FileWalk(rootDir, func(r io.Reader, path string) error { | ||
var erratum RLSA | ||
if err := json.NewDecoder(r).Decode(&erratum); err != nil { | ||
return xerrors.Errorf("failed to decode Rocky erratum: %w", err) | ||
} | ||
|
||
dirs := strings.Split(strings.TrimPrefix(path, rootDir), string(filepath.Separator))[1:] | ||
if len(dirs) != 5 { | ||
log.Printf("Invalid path: %s", path) | ||
return nil | ||
} | ||
|
||
majorVer, repo, arch := dirs[0], dirs[1], dirs[2] | ||
if !utils.StringInSlice(repo, targetRepos) { | ||
log.Printf("Unsupported Rocky repo: %s", repo) | ||
return nil | ||
} | ||
|
||
if !utils.StringInSlice(arch, targetArches) { | ||
switch arch { | ||
case "aarch64": | ||
default: | ||
log.Printf("Unsupported Rocky arch: %s", arch) | ||
} | ||
return nil | ||
} | ||
|
||
errata[majorVer] = append(errata[majorVer], erratum) | ||
return nil | ||
}) | ||
if err != nil { | ||
return xerrors.Errorf("error in Rocky walk: %w", err) | ||
} | ||
|
||
if err = vs.save(errata); err != nil { | ||
return xerrors.Errorf("error in Rocky save: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func (vs VulnSrc) save(errataVer map[string][]RLSA) error { | ||
err := vs.dbc.BatchUpdate(func(tx *bolt.Tx) error { | ||
for majorVer, errata := range errataVer { | ||
platformName := fmt.Sprintf(platformFormat, majorVer) | ||
if err := vs.commit(tx, platformName, errata); err != nil { | ||
return xerrors.Errorf("error in save Rocky %s: %w", majorVer, err) | ||
} | ||
} | ||
return nil | ||
}) | ||
if err != nil { | ||
return xerrors.Errorf("error in db batch update: %w", err) | ||
} | ||
return nil | ||
} | ||
|
||
func (vs VulnSrc) commit(tx *bolt.Tx, platformName string, errata []RLSA) error { | ||
for _, erratum := range errata { | ||
for _, cveID := range erratum.CveIDs { | ||
putAdvisoryCount := 0 | ||
for _, pkg := range erratum.Packages { | ||
// Skip the modular packages until the following bug is fixed. | ||
// https://forums.rockylinux.org/t/some-errata-missing-in-comparison-with-rhel-and-almalinux/3843/8 | ||
if strings.Contains(pkg.Release, ".module+el") { | ||
continue | ||
} | ||
|
||
advisory := types.Advisory{ | ||
FixedVersion: utils.ConstructVersion(pkg.Epoch, pkg.Version, pkg.Release), | ||
} | ||
if err := vs.dbc.PutAdvisoryDetail(tx, cveID, platformName, pkg.Name, advisory); err != nil { | ||
return xerrors.Errorf("failed to save Rocky advisory: %w", err) | ||
} | ||
|
||
putAdvisoryCount++ | ||
} | ||
|
||
if putAdvisoryCount > 0 { | ||
var references []string | ||
for _, ref := range erratum.References { | ||
references = append(references, ref.Href) | ||
} | ||
|
||
vuln := types.VulnerabilityDetail{ | ||
Severity: generalizeSeverity(erratum.Severity), | ||
References: references, | ||
Title: erratum.Title, | ||
Description: erratum.Description, | ||
} | ||
if err := vs.dbc.PutVulnerabilityDetail(tx, cveID, vulnerability.Rocky, vuln); err != nil { | ||
return xerrors.Errorf("failed to save Rocky vulnerability: %w", err) | ||
} | ||
|
||
if err := vs.dbc.PutVulnerabilityID(tx, cveID); err != nil { | ||
return xerrors.Errorf("failed to save the vulnerability ID: %w", err) | ||
} | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (vs VulnSrc) Get(release, pkgName string) ([]types.Advisory, error) { | ||
bucket := fmt.Sprintf(platformFormat, release) | ||
advisories, err := vs.dbc.GetAdvisories(bucket, pkgName) | ||
if err != nil { | ||
return nil, xerrors.Errorf("failed to get Rocky advisories: %w", err) | ||
} | ||
return advisories, nil | ||
} | ||
|
||
func generalizeSeverity(severity string) types.Severity { | ||
switch strings.ToLower(severity) { | ||
case "low": | ||
return types.SeverityLow | ||
case "moderate": | ||
return types.SeverityMedium | ||
case "important": | ||
return types.SeverityHigh | ||
case "critical": | ||
return types.SeverityCritical | ||
} | ||
return types.SeverityUnknown | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
package rocky | ||
|
||
import ( | ||
"path/filepath" | ||
"testing" | ||
|
||
"github.com/stretchr/testify/assert" | ||
"github.com/stretchr/testify/require" | ||
|
||
"github.com/aquasecurity/trivy-db/pkg/db" | ||
"github.com/aquasecurity/trivy-db/pkg/dbtest" | ||
"github.com/aquasecurity/trivy-db/pkg/types" | ||
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability" | ||
) | ||
|
||
func TestVulnSrc_Update(t *testing.T) { | ||
type want struct { | ||
key []string | ||
value interface{} | ||
} | ||
tests := []struct { | ||
name string | ||
dir string | ||
wantValues []want | ||
wantErr string | ||
}{ | ||
{ | ||
name: "happy path", | ||
dir: filepath.Join("testdata", "happy"), | ||
wantValues: []want{ | ||
{ | ||
key: []string{"advisory-detail", "CVE-2021-25215", "rocky 8", "bind-export-libs"}, | ||
value: types.Advisory{ | ||
FixedVersion: "32:9.11.26-4.el8_4", | ||
}, | ||
}, | ||
{ | ||
key: []string{"advisory-detail", "CVE-2021-25215", "rocky 8", "bind-export-devel"}, | ||
value: types.Advisory{ | ||
FixedVersion: "32:9.11.26-4.el8_4", | ||
}, | ||
}, | ||
{ | ||
key: []string{"vulnerability-detail", "CVE-2021-25215", vulnerability.Rocky}, | ||
value: types.VulnerabilityDetail{ | ||
Severity: types.SeverityHigh, | ||
References: []string{ | ||
"https://access.redhat.com/hydra/rest/securitydata/cve/CVE-2021-25215.json", | ||
}, | ||
Title: "Important: bind security update", | ||
Description: "For more information visit https://errata.rockylinux.org/RLSA-2021:1989", | ||
}, | ||
}, | ||
{ | ||
key: []string{"vulnerability-id", "CVE-2021-25215"}, | ||
value: map[string]interface{}{}, | ||
}, | ||
}, | ||
}, | ||
{ | ||
name: "skip advisories for modular package", | ||
dir: filepath.Join("testdata", "modular"), | ||
wantValues: []want{}, | ||
}, | ||
{ | ||
name: "sad path", | ||
dir: filepath.Join("testdata", "sad"), | ||
wantErr: "failed to decode Rocky erratum", | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
tempDir := t.TempDir() | ||
|
||
err := db.Init(tempDir) | ||
require.NoError(t, err) | ||
defer db.Close() | ||
|
||
vs := NewVulnSrc() | ||
err = vs.Update(tt.dir) | ||
if tt.wantErr != "" { | ||
require.Error(t, err) | ||
assert.Contains(t, err.Error(), tt.wantErr) | ||
return | ||
} | ||
|
||
require.NoError(t, err) | ||
require.NoError(t, db.Close()) // Need to close before dbtest.JSONEq is called | ||
for _, want := range tt.wantValues { | ||
dbtest.JSONEq(t, db.Path(tempDir), want.key, want.value) | ||
} | ||
}) | ||
} | ||
} |
Oops, something went wrong.