Skip to content

Commit

Permalink
feat(wrlinux): Add Wind River Linux support (#259)
Browse files Browse the repository at this point in the history
Signed-off-by: Sakib Sajal <[email protected]>
  • Loading branch information
sajal-wr committed Apr 11, 2023
1 parent 9692a42 commit a0eb340
Show file tree
Hide file tree
Showing 7 changed files with 336 additions and 1 deletion.
1 change: 1 addition & 0 deletions pkg/vulnsrc/vulnerability/const.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const (
Alma types.SourceID = "alma"
CBLMariner types.SourceID = "cbl-mariner"
Photon types.SourceID = "photon"
WRLinux types.SourceID = "wrlinux"
RubySec types.SourceID = "ruby-advisory-db"
PhpSecurityAdvisories types.SourceID = "php-security-advisories"
NodejsSecurityWg types.SourceID = "nodejs-security-wg"
Expand Down
2 changes: 1 addition & 1 deletion pkg/vulnsrc/vulnerability/vulnerability.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ const (
)

var (
sources = []types.SourceID{NVD, RedHat, Debian, Ubuntu, Alpine, Amazon, OracleOVAL, SuseCVRF, Photon,
sources = []types.SourceID{NVD, RedHat, Debian, Ubuntu, Alpine, Amazon, OracleOVAL, SuseCVRF, Photon, WRLinux,
ArchLinux, Alma, Rocky, CBLMariner, RubySec, PhpSecurityAdvisories, NodejsSecurityWg, GoVulnDB, GHSA, GLAD, OSV,
}
)
Expand Down
2 changes: 2 additions & 0 deletions pkg/vulnsrc/vulnsrc.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import (
susecvrf "github.com/aquasecurity/trivy-db/pkg/vulnsrc/suse-cvrf"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/ubuntu"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/wolfi"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/wrlinux"
)

type VulnSrc interface {
Expand Down Expand Up @@ -53,6 +54,7 @@ var (
photon.NewVulnSrc(),
mariner.NewVulnSrc(),
wolfi.NewVulnSrc(),
wrlinux.NewVulnSrc(),

// Language-specific packages
bundler.NewVulnSrc(),
Expand Down
29 changes: 29 additions & 0 deletions pkg/vulnsrc/wrlinux/testdata/vuln-list/wrlinux/CVE-2020-24241.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"Candidate": "CVE-2020-24241",
"PublicDate": "2020-08-25T00:00:00Z",
"Description": "In Netwide Assembler (NASM) 2.15rc10, there is heap use-after-free in saa_wbytes in nasmlib/saa.c.",
"Notes": null,
"Priority": "medium",
"Bugs": [
"LINCD-2974",
"LIN1019-5289",
"LIN1018-6614",
"LIN10-7689"
],
"Patches": {
"nasm": {
"10.18.44.1": {
"Status": "pending",
"Note": ""
},
"10.19.45.1": {
"Status": "released",
"Note": "10.19.45.11"
},
"10.20.6.0": {
"Status": "not-affected",
"Note": ""
}
}
}
}
18 changes: 18 additions & 0 deletions pkg/vulnsrc/wrlinux/types.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package wrlinux

type WRLinuxCVE struct {
Description string `json:"description"`
Candidate string
Priority string
Patches map[PackageName]Patch
References []string
}

type PackageName string
type Release string
type Patch map[Release]Status

type Status struct {
Status string
Note string
}
199 changes: 199 additions & 0 deletions pkg/vulnsrc/wrlinux/wrlinux.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
/*
* Copyright (c) 2022 Wind River Systems, Inc.
*
* The right to copy, distribute, modify, or otherwise make use
* of this software may be licensed only pursuant to the terms
* of an applicable Wind River license agreement.
*/

package wrlinux

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"
ustrings "github.com/aquasecurity/trivy-db/pkg/utils/strings"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
)

const (
wrlinuxDir = "wrlinux"
platformFormat = "WRLinux OS %s"
)

var (
targetStatuses = []string{"pending", "released"}

source = types.DataSource{
ID: vulnerability.WRLinux,
Name: "WRLinux OS CVE metadata",
URL: "https://support2.windriver.com",
}
)

type Option func(src *VulnSrc)

func WithCustomPut(put db.CustomPut) Option {
return func(src *VulnSrc) {
src.put = put
}
}

type VulnSrc struct {
put db.CustomPut
dbc db.Operation
}

func NewVulnSrc(opts ...Option) VulnSrc {
src := VulnSrc{
put: defaultPut,
dbc: db.Config{},
}

for _, o := range opts {
o(&src)
}

return src
}

func (vs VulnSrc) Name() types.SourceID {
return source.ID
}

func (vs VulnSrc) Update(dir string) error {
rootDir := filepath.Join(dir, "vuln-list", wrlinuxDir)
var cves []WRLinuxCVE
err := utils.FileWalk(rootDir, func(r io.Reader, path string) error {
var cve WRLinuxCVE
if err := json.NewDecoder(r).Decode(&cve); err != nil {
return xerrors.Errorf("failed to decode WRLinux JSON: %w", err)
}
cves = append(cves, cve)
return nil
})
if err != nil {
return xerrors.Errorf("error in wrlinux walk: %w", err)
}

if err = vs.save(cves); err != nil {
return xerrors.Errorf("error in wrlinux save: %w", err)
}

return nil
}

func (vs VulnSrc) save(cves []WRLinuxCVE) error {
log.Println("Saving wrlinux DB")
err := vs.dbc.BatchUpdate(func(tx *bolt.Tx) error {
err := vs.commit(tx, cves)
if err != nil {
return err
}
return nil
})
if err != nil {
return xerrors.Errorf("error in batch update: %w", err)
}
return nil
}

func (vs VulnSrc) commit(tx *bolt.Tx, cves []WRLinuxCVE) error {
for _, cve := range cves {
if err := vs.put(vs.dbc, tx, cve); err != nil {
return xerrors.Errorf("put error: %w", err)
}
}
return nil
}

func (vs VulnSrc) Get(osVer string, pkgName string) ([]types.Advisory, error) {
bucket := fmt.Sprintf(platformFormat, OsVerToRelease(osVer))
advisories, err := vs.dbc.GetAdvisories(bucket, pkgName)
if err != nil {
return nil, xerrors.Errorf("failed to get wrlinux advisories: %w", err)
}
return advisories, nil
}

func defaultPut(dbc db.Operation, tx *bolt.Tx, advisory interface{}) error {
cve, ok := advisory.(WRLinuxCVE)
if !ok {
return xerrors.New("unknown type")
}
for packageName, patch := range cve.Patches {
pkgName := string(packageName)
for osVer, status := range patch {
if !ustrings.InSlice(status.Status, targetStatuses) {
continue
}
release := OsVerToRelease(string(osVer))
platformName := fmt.Sprintf(platformFormat, release)
if err := dbc.PutDataSource(tx, platformName, source); err != nil {
return xerrors.Errorf("failed to put data source: %w", err)
}

adv := types.Advisory{}
if status.Status == "released" {
adv.FixedVersion = status.Note
}
if err := dbc.PutAdvisoryDetail(tx, cve.Candidate, pkgName, []string{platformName}, adv); err != nil {
return xerrors.Errorf("failed to save wrlinux advisory: %w", err)
}

vuln := types.VulnerabilityDetail{
Severity: SeverityFromPriority(cve.Priority),
References: cve.References,
Description: cve.Description,
}
if err := dbc.PutVulnerabilityDetail(tx, cve.Candidate, source.ID, vuln); err != nil {
return xerrors.Errorf("failed to save wrlinux vulnerability: %w", err)
}

// for optimization
if err := dbc.PutVulnerabilityID(tx, cve.Candidate); err != nil {
return xerrors.Errorf("failed to save the vulnerability ID: %w", err)
}
}
}

return nil
}

// SeverityFromPriority converts wrlinux priority into Trivy severity
func SeverityFromPriority(priority string) types.Severity {
switch priority {
case "new":
return types.SeverityUnknown
case "negligible", "low":
return types.SeverityLow
case "medium":
return types.SeverityMedium
case "high":
return types.SeverityHigh
case "critical":
return types.SeverityCritical
default:
return types.SeverityUnknown
}
}

// gets the release from the osVersion
// "w.x.y.z" -> "w.x"
func OsVerToRelease(osVer string) string {
s := strings.Split(osVer, ".")
if s[len(s)-1] == "0" {
return "LINCD"
}
return strings.Join(s[:2], ".")
}
86 changes: 86 additions & 0 deletions pkg/vulnsrc/wrlinux/wrlinux_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
/*
* Copyright (c) 2022 Wind River Systems, Inc.
*
* The right to copy, distribute, modify, or otherwise make use
* of this software may be licensed only pursuant to the terms
* of an applicable Wind River license agreement.
*/

package wrlinux_test

import (
"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/wrlinux"
"github.com/aquasecurity/trivy-db/pkg/vulnsrc/vulnerability"
)

func TestVulnSrc_Update(t *testing.T) {
type wantKV struct {
key []string
value interface{}
}
tests := []struct {
name string
statuses []string
wantValues []wantKV
noBuckets [][]string
wantErr string
}{
{
name: "happy path",
wantValues: []wantKV{
{
key: []string{"data-source", "WRLinux OS 10.19"},
value: types.DataSource{
ID: vulnerability.WRLinux,
Name: "WRLinux OS CVE metadata",
URL: "https://support2.windriver.com",
},
},
{
key: []string{"advisory-detail", "CVE-2020-24241", "WRLinux OS 10.19", "nasm"},
value: types.Advisory{
FixedVersion: "10.19.45.11",
},
},
{
key: []string{"vulnerability-detail", "CVE-2020-24241", "wrlinux"},
value: types.VulnerabilityDetail{
Description: "In Netwide Assembler (NASM) 2.15rc10, there is heap use-after-free in saa_wbytes in nasmlib/saa.c.",
Severity: 2,
References: []string{},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
cacheDir := dbtest.InitDB(t, nil)

src := wrlinux.NewVulnSrc()
err := src.Update("testdata")
if tt.wantErr != "" {
require.Error(t, err)
assert.Contains(t, err.Error(), tt.wantErr, tt.name)
return
}

require.NoError(t, err, tt.name)

// Compare DB entries
require.NoError(t, err, db.Close())
dbPath := db.Path(cacheDir)
for _, want := range tt.wantValues {
dbtest.JSONEq(t, dbPath, want.key, want.value)
}
})
}
}

0 comments on commit a0eb340

Please sign in to comment.