From 88d9f779d75b4660399f2490d949cb764d89631c Mon Sep 17 00:00:00 2001 From: Sakib Sajal Date: Thu, 17 Feb 2022 11:22:54 -0500 Subject: [PATCH] feat(wrlinux): Add Wind River Linux vulnerability data (#177) Signed-off-by: Sakib Sajal --- .github/workflows/update.yml | 4 + README.md | 2 +- main.go | 8 +- wrlinux/testdata/multiple_multiline_note | 19 ++ wrlinux/testdata/multiple_packages | 19 ++ .../testdata/multiple_references_and_notes | 22 ++ wrlinux/testdata/no_references_or_notes | 17 ++ .../testdata/with_comments_and_line_breaks | 28 ++ wrlinux/wrlinux.go | 280 ++++++++++++++++++ wrlinux/wrlinux_test.go | 241 +++++++++++++++ 10 files changed, 638 insertions(+), 2 deletions(-) create mode 100644 wrlinux/testdata/multiple_multiline_note create mode 100644 wrlinux/testdata/multiple_packages create mode 100644 wrlinux/testdata/multiple_references_and_notes create mode 100644 wrlinux/testdata/no_references_or_notes create mode 100644 wrlinux/testdata/with_comments_and_line_breaks create mode 100755 wrlinux/wrlinux.go create mode 100644 wrlinux/wrlinux_test.go diff --git a/.github/workflows/update.yml b/.github/workflows/update.yml index 2e1f1421..a7dda151 100644 --- a/.github/workflows/update.yml +++ b/.github/workflows/update.yml @@ -99,6 +99,10 @@ jobs: name: CBL-Mariner Vulnerability Data run: ./vuln-list-update -target mariner + - if: always() + name: WindRiver CVE Tracker + run: ./vuln-list-update -target wrlinux + - if: always() name: OSV Database run: ./vuln-list-update -target osv diff --git a/README.md b/README.md index fb30634b..b9793d87 100644 --- a/README.md +++ b/README.md @@ -20,7 +20,7 @@ https://github.com/aquasecurity/vuln-list/ $ vuln-list-update -h Usage of vuln-list-update: -target string - update target (nvd, alpine, alpine-unfixed, redhat, redhat-oval, debian, debian-oval, ubuntu, amazon, oracle-oval, suse-cvrf, photon, arch-linux, ghsa, glad, cwe, osv, go-vulndb, mariner, kevc) + update target (nvd, alpine, alpine-unfixed, redhat, redhat-oval, debian, debian-oval, ubuntu, amazon, oracle-oval, suse-cvrf, photon, arch-linux, ghsa, glad, cwe, osv, go-vulndb, mariner, kevc, wrlinux) -years string update years (only redhat) ``` diff --git a/main.go b/main.go index 4911ef92..c122aaab 100644 --- a/main.go +++ b/main.go @@ -39,6 +39,7 @@ import ( susecvrf "github.com/aquasecurity/vuln-list-update/suse/cvrf" "github.com/aquasecurity/vuln-list-update/ubuntu" "github.com/aquasecurity/vuln-list-update/utils" + "github.com/aquasecurity/vuln-list-update/wrlinux" ) const ( @@ -49,7 +50,7 @@ const ( var ( target = flag.String("target", "", "update target (nvd, alpine, alpine-unfixed, redhat, redhat-oval, "+ - "debian, debian-oval, ubuntu, amazon, oracle-oval, suse-cvrf, photon, arch-linux, ghsa, glad, cwe, osv, go-vulndb, mariner, kevc, wolfi)") + "debian, debian-oval, ubuntu, amazon, oracle-oval, suse-cvrf, photon, arch-linux, ghsa, glad, cwe, osv, go-vulndb, mariner, kevc, wolfi, wrlinux)") years = flag.String("years", "", "update years (only redhat)") targetUri = flag.String("target-uri", "", "alternative repository URI (only glad)") targetBranch = flag.String("target-branch", "", "alternative repository branch (only glad)") @@ -235,6 +236,11 @@ func run() error { return xerrors.Errorf("Wolfi update error: %w", err) } commitMsg = "Wolfi Issue Tracker" + case "wrlinux": + if err := wrlinux.Update(); err != nil { + return xerrors.Errorf("WRLinux update error: %w", err) + } + commitMsg = "Wind River CVE Tracker" default: return xerrors.New("unknown target") } diff --git a/wrlinux/testdata/multiple_multiline_note b/wrlinux/testdata/multiple_multiline_note new file mode 100644 index 00000000..4844129c --- /dev/null +++ b/wrlinux/testdata/multiple_multiline_note @@ -0,0 +1,19 @@ +Candidate: CVE-2012-0880 +PublicDate: 2017-08-08 +Description: + Apache Xerces-C++ allows remote attackers to cause a denial of + service (CPU consumption) via a crafted message sent to an XML + service that causes hash table collisions. +Notes: + note 1 line 1 + note 1 line 2 + note 2 line 1 + note 2 line 2 +Priority: high +Bugs: + LIN10-1106 + +Patches_xerces: +10.17.41.1_xerces: released (10.17.41.1) +10.18.44.1_xerces: ignored (will not fix) +10.19.45.1_xerces: ignored (will not fix) diff --git a/wrlinux/testdata/multiple_packages b/wrlinux/testdata/multiple_packages new file mode 100644 index 00000000..b962b05b --- /dev/null +++ b/wrlinux/testdata/multiple_packages @@ -0,0 +1,19 @@ +Candidate: CVE-2015-8985 +PublicDate: 2017-03-20 +Description: + The pop_fail_stack function in the GNU C Library (aka glibc or + libc6) allows context-dependent attackers to cause a denial of + service (assertion failure and application crash) via vectors + related to extended regular expression processing. +Notes: + glibc +Priority: medium +Bugs: + +Patches_glibc: +10.18.44.1_glibc: pending +10.19.45.1_glibc: pending + +Patches_eglibc: +10.18.44.1_eglibc: pending +10.19.45.1_eglibc: pending diff --git a/wrlinux/testdata/multiple_references_and_notes b/wrlinux/testdata/multiple_references_and_notes new file mode 100644 index 00000000..8d8657dd --- /dev/null +++ b/wrlinux/testdata/multiple_references_and_notes @@ -0,0 +1,22 @@ +Candidate: CVE-2021-39648 +PublicDate: 2021-12-15 +Description: + In gadget_dev_desc_UDC_show of configfs.c, there is a possible + disclosure of kernel heap memory due to a race condition. +References: + Upstream kernel + Upstream linux +Notes: + This could lead to local information disclosure with System execution privileges needed. + User interaction is not needed for exploitation. +Priority: medium +Bugs: + LINCD-7525 + LIN1021-2165 + LIN1019-7478 + LIN1018-8466 +Patches_linux: +10.20.6.0_linux: not-affected +10.21.20.1_linux: not-affected +10.19.45.1_linux: released (10.19.45.21) +10.18.44.1_linux: released (10.18.44.25) diff --git a/wrlinux/testdata/no_references_or_notes b/wrlinux/testdata/no_references_or_notes new file mode 100644 index 00000000..787cd1b5 --- /dev/null +++ b/wrlinux/testdata/no_references_or_notes @@ -0,0 +1,17 @@ +Candidate: CVE-2020-24241 +PublicDate: 2020-08-25 +Description: + In Netwide Assembler (NASM) 2.15rc10, there is heap use-after-free + in saa_wbytes in nasmlib/saa.c. +Priority: medium +Bugs: + LINCD-2974 + LIN1019-5289 + LIN1018-6614 + LIN10-7689 + +Patches_nasm: +10.20.6.0_nasm: not-affected +10.19.45.1_nasm: pending +10.18.44.1_nasm: ignored +10.17.41.1_nasm: released (10.17.41.22) diff --git a/wrlinux/testdata/with_comments_and_line_breaks b/wrlinux/testdata/with_comments_and_line_breaks new file mode 100644 index 00000000..ae97b627 --- /dev/null +++ b/wrlinux/testdata/with_comments_and_line_breaks @@ -0,0 +1,28 @@ +Candidate: CVE-2022-3134 + +PublicDate: 2022-09-06 + +Description: + Use After Free in GitHub repository vim/vim prior to 9.0.0389. + +Notes: + +Priority: high + +Bugs: + LINCD-10301 + LIN1022-1711 + LIN1021-4364 + LIN1019-8796 + LIN1018-9727 + +# fixes/patches for different WRLinux releases +# _: [()] +Patches_vim: +10.20.6.0_vim: not-affected +10.22.33.1_vim: not-affected +# the following have releases have been fixed +10.21.20.1_vim: released (10.21.20.14) +10.19.45.1_vim: released (10.19.45.26) + +10.18.44.1_vim: released (10.18.44.28) diff --git a/wrlinux/wrlinux.go b/wrlinux/wrlinux.go new file mode 100755 index 00000000..ba7ab28a --- /dev/null +++ b/wrlinux/wrlinux.go @@ -0,0 +1,280 @@ +/* + * 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 ( + "io" + "bufio" + "log" + "os" + "path/filepath" + "strings" + "time" + + "github.com/aquasecurity/vuln-list-update/git" + "github.com/araddon/dateparse" + "golang.org/x/xerrors" + "github.com/aquasecurity/vuln-list-update/utils" +) + +const ( + cveTrackerDir = "windriver-cve-tracker" + windriverDir = "wrlinux" +) + +var ( + repoURLs = []string{ + "https://distro.windriver.com/git/windriver-cve-tracker.git", + } + targets = []string{ + "active", + } + statuses = []string{ + "released", + "pending", + "not-affected", + "ignored", + } +) + +type Vulnerability struct { + Candidate string + PublicDate time.Time + Description string + References []string + Notes []string + Priority string + Bugs []string + Patches map[Package]Statuses +} + +type Package string + +type Release string + +type Statuses map[Release]Status + +type Status struct { + Status string + Note string +} + +func Update() error { + var err error + gc := git.Config{} + dir := filepath.Join(utils.CacheDir(), cveTrackerDir) + for _, url := range repoURLs { + _, err = gc.CloneOrPull(url, dir, "master", false) + if err == nil { + break + } + log.Printf("failed to clone or pull: %s: %v", url, err) + log.Printf("removing %s directory", cveTrackerDir) + if err := os.RemoveAll(dir); err != nil { + return xerrors.Errorf("failed to remove %s directory: %w", cveTrackerDir, err) + } + } + if err != nil { + return xerrors.Errorf("failed to clone or pull: %w", err) + } + + dst := filepath.Join(utils.VulnListDir(), windriverDir) + log.Printf("removing windriver directory %s", dst) + if err := os.RemoveAll(dst); err != nil { + return xerrors.Errorf("failed to remove windriver directory: %w", err) + } + + log.Println("walking windriver-cve-tracker ...") + for _, target := range targets { + if err := walkDir(filepath.Join(dir, target)); err != nil { + return err + } + } + + return nil +} + +func walkDir(root string) error { + err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error { + if err != nil { + return xerrors.Errorf("file walk error: %w", err) + } + if info.IsDir() { + return nil + } + + base := filepath.Base(path) + if !strings.HasPrefix(base, "CVE-") { + return nil + } + + f, err := os.Open(path) + if err != nil { + return xerrors.Errorf("error in file open: %w", err) + } + vuln, err := parse(f) + if err != nil { + return xerrors.Errorf("error in parse: %w", err) + } + + if err = utils.SaveCVEPerYear(filepath.Join(utils.VulnListDir(), windriverDir), vuln.Candidate, vuln); err != nil { + return xerrors.Errorf("error in save: %w", err) + } + + return nil + }) + + if err != nil { + return xerrors.Errorf("error in walk: %w", err) + } + return nil +} + +func parse(r io.Reader) (vuln *Vulnerability, err error) { + vuln = &Vulnerability{} + vuln.Patches = map[Package]Statuses{} + + lines := []string{} + scanner := bufio.NewScanner(r) + for scanner.Scan() { + lines = append(lines, scanner.Text()) + } + + for i := 0; i < len(lines); i++ { + line := lines[i] + + // Skip + if strings.HasPrefix(line, "#") || line == "" { + continue + } + + // Parse Candidate + if strings.HasPrefix(line, "Candidate:") { + line = strings.TrimPrefix(line, "Candidate:") + vuln.Candidate = strings.TrimSpace(line) + continue + } + + // Parse PublicDate + if strings.HasPrefix(line, "PublicDate:") { + line = strings.TrimPrefix(line, "PublicDate:") + line = strings.TrimSpace(line) + vuln.PublicDate, _ = dateparse.ParseAny(line) + continue + } + + // Parse Description + if strings.HasPrefix(line, "Description:") { + var description []string + for strings.HasPrefix(lines[i+1], " ") { + i++ + line = strings.TrimSpace(lines[i]) + description = append(description, line) + } + vuln.Description = strings.Join(description, " ") + continue + } + + // Parse References + if strings.HasPrefix(line, "References:") { + for strings.HasPrefix(lines[i+1], " ") { + i++ + line = strings.TrimSpace(lines[i]) + vuln.References = append(vuln.References, line) + } + continue + } + + // Parse Notes + if strings.HasPrefix(line, "Notes:") { + for strings.HasPrefix(lines[i+1], " ") { + i++ + line = strings.TrimSpace(lines[i]) + note := []string{line} + for strings.HasPrefix(lines[i+1], " ") { + i++ + l := strings.TrimSpace(lines[i]) + note = append(note, l) + } + vuln.Notes = append(vuln.Notes, strings.Join(note, " ")) + } + continue + } + + // Parse Priority + if strings.HasPrefix(line, "Priority:") { + line = strings.TrimPrefix(line, "Priority:") + vuln.Priority = strings.TrimSpace(line) + continue + } + + // Parse Bugs + if strings.HasPrefix(line, "Bugs:") { + for strings.HasPrefix(lines[i+1], " ") { + i++ + line = strings.TrimSpace(lines[i]) + vuln.Bugs = append(vuln.Bugs, line) + } + continue + } + + // Parse Patches, this indicates if a CVE has been fixed, if so, in which release. + // eg: 10.21.20.1_vim: released (10.21.20.14) + // _: + // where status: [(note)] + // release: 10.21.20.1 + // package: vim + // status: released + // note: 10.21.20.14 (fixed release) + s := strings.SplitN(line, ":", 2) + if len(s) < 2 { + continue + } + + status := strings.TrimSpace(s[1]) + if isPatch(status) && !strings.HasPrefix(s[0], "Patches_") { + pkgRel := strings.SplitN(s[0], "_", 2) + release := Release(pkgRel[0]) + pkgName := Package(pkgRel[1]) + + fields := strings.Fields(status) + status := Status{ + Status: fields[0], + } + // status is any of: pending/ignored/released/not-affected + // followed by optional note in () + // if the status contains multiple fields, + // it also has the release in which it was fixed. + // ie, released (10.21.20.14) + if len(fields) > 1 { + note := strings.Join(fields[1:], " ") + status.Note = strings.Trim(note, "()") + } + + if existingStatuses, ok := vuln.Patches[pkgName]; ok { + existingStatuses[release] = status + vuln.Patches[pkgName] = existingStatuses + } else { + statuses := Statuses{} + statuses[release] = status + vuln.Patches[pkgName] = statuses + } + } + } + return vuln, nil +} + +func isPatch(s string) bool { + for _, status := range statuses { + if strings.HasPrefix(s, status) { + return true + } + } + return false +} diff --git a/wrlinux/wrlinux_test.go b/wrlinux/wrlinux_test.go new file mode 100644 index 00000000..69a45a4e --- /dev/null +++ b/wrlinux/wrlinux_test.go @@ -0,0 +1,241 @@ +/* + * 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 ( + "os" + "testing" + "time" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func Test_parse(t *testing.T) { + type args struct { + filePath string + } + testCases := []struct { + name string + args args + want *Vulnerability + wantErr error + }{ + { + name: "no references or notes", + args: args{ + filePath: "./testdata/no_references_or_notes", + }, + want: &Vulnerability{ + Candidate: "CVE-2020-24241", + PublicDate: time.Date(2020, 8, 25, 0, 0, 0, 0, time.UTC), + Description: "In Netwide Assembler (NASM) 2.15rc10, there is heap use-after-free in saa_wbytes in nasmlib/saa.c.", + Priority: "medium", + Bugs: []string{ + "LINCD-2974", + "LIN1019-5289", + "LIN1018-6614", + "LIN10-7689", + }, + Patches: map[Package]Statuses{ + Package("nasm"): { + "10.17.41.1": { + Status: "released", + Note: "10.17.41.22", + }, + "10.18.44.1": { + Status: "ignored", + Note: "", + }, + "10.19.45.1": { + Status: "pending", + Note: "", + }, + "10.20.6.0": { + Status: "not-affected", + Note: "", + }, + }, + }, + }, + }, + { + name: "multiple references and notes", + args: args{ + filePath: "./testdata/multiple_references_and_notes", + }, + want: &Vulnerability{ + Candidate: "CVE-2021-39648", + PublicDate: time.Date(2021, 12, 15, 0, 0, 0, 0, time.UTC), + Description: "In gadget_dev_desc_UDC_show of configfs.c, there is a possible disclosure of kernel heap memory due to a race condition.", + References: []string{ + "Upstream kernel", + "Upstream linux", + }, + Notes: []string{ + "This could lead to local information disclosure with System execution privileges needed.", + "User interaction is not needed for exploitation.", + }, + Priority: "medium", + Bugs: []string{ + "LINCD-7525", + "LIN1021-2165", + "LIN1019-7478", + "LIN1018-8466", + }, + Patches: map[Package]Statuses{ + Package("linux"): { + "10.18.44.1": { + Status: "released", + Note: "10.18.44.25", + }, + "10.19.45.1": { + Status: "released", + Note: "10.19.45.21", + }, + "10.20.6.0": { + Status: "not-affected", + Note: "", + }, + "10.21.20.1": { + Status: "not-affected", + Note: "", + }, + }, + }, + }, + }, + { + name: "multiple packages", + args: args{ + filePath: "./testdata/multiple_packages", + }, + want: &Vulnerability{ + Candidate: "CVE-2015-8985", + PublicDate: time.Date(2017, 3, 20, 0, 0, 0, 0, time.UTC), + Description: "The pop_fail_stack function in the GNU C Library (aka glibc or libc6) allows context-dependent attackers to cause a denial of service (assertion failure and application crash) via vectors related to extended regular expression processing.", + Notes: []string{ + "glibc", + }, + Priority: "medium", + Patches: map[Package]Statuses{ + Package("glibc"): { + "10.18.44.1": { + Status: "pending", + Note: "", + }, + "10.19.45.1": { + Status: "pending", + Note: "", + }, + }, + Package("eglibc"): { + "10.18.44.1": { + Status: "pending", + Note: "", + }, + "10.19.45.1": { + Status: "pending", + Note: "", + }, + }, + }, + }, + }, + { + name: "with comments and line breaks", + args: args{ + filePath: "./testdata/with_comments_and_line_breaks", + }, + want: &Vulnerability{ + Candidate: "CVE-2022-3134", + PublicDate: time.Date(2022, 9, 6, 0, 0, 0, 0, time.UTC), + Description: "Use After Free in GitHub repository vim/vim prior to 9.0.0389.", + Priority: "high", + Bugs: []string{ + "LINCD-10301", + "LIN1022-1711", + "LIN1021-4364", + "LIN1019-8796", + "LIN1018-9727", + }, + Patches: map[Package]Statuses{ + Package("vim"): { + "10.18.44.1": { + Status: "released", + Note: "10.18.44.28", + }, + "10.19.45.1": { + Status: "released", + Note: "10.19.45.26", + }, + "10.20.6.0": { + Status: "not-affected", + Note: "", + }, + "10.21.20.1": { + Status: "released", + Note: "10.21.20.14", + }, + "10.22.33.1": { + Status: "not-affected", + Note: "", + }, + }, + }, + }, + }, + { + name: "multiple multiline note", + args: args{ + filePath: "./testdata/multiple_multiline_note", + }, + want: &Vulnerability{ + Candidate: "CVE-2012-0880", + PublicDate: time.Date(2017, 8, 8, 0, 0, 0, 0, time.UTC), + Description: "Apache Xerces-C++ allows remote attackers to cause a denial of service (CPU consumption) via a crafted message sent to an XML service that causes hash table collisions.", + Priority: "high", + Notes: []string{ + "note 1 line 1 note 1 line 2", + "note 2 line 1 note 2 line 2", + }, + Bugs: []string{ + "LIN10-1106", + }, + Patches: map[Package]Statuses{ + Package("xerces"): { + "10.17.41.1": { + Status: "released", + Note: "10.17.41.1", + }, + "10.18.44.1": { + Status: "ignored", + Note: "will not fix", + }, + "10.19.45.1": { + Status: "ignored", + Note: "will not fix", + }, + }, + }, + }, + }, + } + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + f, err := os.Open(tc.args.filePath) + require.NoError(t, err) + defer f.Close() + + got, gotErr := parse(f) + assert.Equal(t, tc.wantErr, gotErr) + assert.Equal(t, tc.want, got) + }) + } +}