Skip to content

Commit

Permalink
feat(rocky): support Rocky Linux (#154)
Browse files Browse the repository at this point in the history
Co-authored-by: knqyf263 <[email protected]>
  • Loading branch information
MaineK00n and knqyf263 authored Jan 19, 2022
1 parent 1a83ba8 commit 3e43e9a
Show file tree
Hide file tree
Showing 12 changed files with 469 additions and 31 deletions.
15 changes: 15 additions & 0 deletions pkg/utils/utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package utils

import (
"bytes"
"fmt"
"io"
"io/fs"
"log"
Expand Down Expand Up @@ -118,3 +119,17 @@ func IsInt(s string) bool {
_, err := strconv.Atoi(s)
return err == nil
}

func ConstructVersion(epoch, version, release string) string {
verStr := ""
if epoch != "0" && epoch != "" {
verStr += fmt.Sprintf("%s:", epoch)
}
verStr += version

if release != "" {
verStr += fmt.Sprintf("-%s", release)

}
return verStr
}
16 changes: 1 addition & 15 deletions pkg/vulnsrc/alma/alma.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ func (vs VulnSrc) commit(tx *bolt.Tx, platformName string, errata []Erratum) err
}

advisory := types.Advisory{
FixedVersion: constructVersion(pkg.Epoch, pkg.Version, pkg.Release),
FixedVersion: utils.ConstructVersion(pkg.Epoch, pkg.Version, pkg.Release),
}

if adv, ok := advisories[pkgName]; ok {
Expand Down Expand Up @@ -174,17 +174,3 @@ func generalizeSeverity(severity string) types.Severity {
}
return types.SeverityUnknown
}

func constructVersion(epoch, version, release string) string {
verStr := ""
if epoch != "0" && epoch != "" {
verStr += fmt.Sprintf("%s:", epoch)
}
verStr += version

if release != "" {
verStr += fmt.Sprintf("-%s", release)

}
return verStr
}
16 changes: 1 addition & 15 deletions pkg/vulnsrc/amazon/amazon.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (vs VulnSrc) commit(tx *bolt.Tx) error {
for _, pkg := range alas.Packages {
platformName := fmt.Sprintf(platformFormat, majorVersion)
advisory := types.Advisory{
FixedVersion: constructVersion(pkg.Epoch, pkg.Version, pkg.Release),
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 Amazon advisory: %w", err)
Expand Down Expand Up @@ -176,17 +176,3 @@ func severityFromPriority(priority string) types.Severity {
return types.SeverityUnknown
}
}

func constructVersion(epoch, version, release string) string {
verStr := ""
if epoch != "0" && epoch != "" {
verStr += fmt.Sprintf("%s:", epoch)
}
verStr += version

if release != "" {
verStr += fmt.Sprintf("-%s", release)

}
return verStr
}
171 changes: 171 additions & 0 deletions pkg/vulnsrc/rocky/rocky.go
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
}
94 changes: 94 additions & 0 deletions pkg/vulnsrc/rocky/rocky_test.go
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)
}
})
}
}
Loading

0 comments on commit 3e43e9a

Please sign in to comment.