blob: 5b7e6589efadca0584687701d5da2cd605e3b596 [file] [log] [blame]
Julie Qiu5b3cf6b2021-12-20 16:26:47 -05001// Copyright 2021 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5// Package report contains functionality for parsing and linting YAML reports
6// in reports/.
7package report
8
Jonathan Amsterdam566ba412022-01-20 08:40:49 -05009import (
10 "fmt"
Jonathan Amsterdamb63dc7c2022-01-20 09:40:07 -050011 "io"
Jonathan Amsterdam566ba412022-01-20 08:40:49 -050012 "os"
Jonathan Amsterdamb63dc7c2022-01-20 09:40:07 -050013 "strings"
Jonathan Amsterdam566ba412022-01-20 08:40:49 -050014 "time"
15
16 "golang.org/x/vulndb/internal/derrors"
Jonathan Amsterdamb63dc7c2022-01-20 09:40:07 -050017 "gopkg.in/yaml.v3"
Jonathan Amsterdam566ba412022-01-20 08:40:49 -050018)
Julie Qiu5b3cf6b2021-12-20 16:26:47 -050019
20type VersionRange struct {
Julie Qiu89796ae2021-12-22 16:57:08 -050021 Introduced string `yaml:"introduced,omitempty"`
22 Fixed string `yaml:"fixed,omitempty"`
Julie Qiu5b3cf6b2021-12-20 16:26:47 -050023}
24
25type Additional struct {
26 Module string `yaml:",omitempty"`
27 Package string `yaml:",omitempty"`
28 Symbols []string `yaml:",omitempty"`
29 Versions []VersionRange `yaml:",omitempty"`
30}
31
32type Links struct {
33 PR string `yaml:",omitempty"`
34 Commit string `yaml:",omitempty"`
35 Context []string `yaml:",omitempty"`
36}
37
38type CVEMeta struct {
39 ID string `yaml:",omitempty"`
40 CWE string `yaml:",omitempty"`
41 Description string `yaml:",omitempty"`
42}
43
44type Report struct {
45 Module string `yaml:",omitempty"`
46 Package string `yaml:",omitempty"`
47 // TODO: could also be GoToolchain, but we might want
48 // this for other things?
49 //
50 // could we also automate this by just looking for
51 // things prefixed with cmd/go?
52 DoNotExport bool `yaml:"do_not_export,omitempty"`
Julie Qiu5b3cf6b2021-12-20 16:26:47 -050053 // TODO: the most common usage of additional package should
54 // really be replaced with 'aliases', we'll still need
55 // additional packages for some cases, but it's too heavy
56 // for most
57 AdditionalPackages []Additional `yaml:"additional_packages,omitempty"`
Julie Qiu89796ae2021-12-22 16:57:08 -050058 Versions []VersionRange `yaml:"versions,omitempty"`
Julie Qiu5b3cf6b2021-12-20 16:26:47 -050059
60 // Description is the CVE description from an existing CVE. If we are
61 // assigning a CVE ID ourselves, use CVEMetadata.Description instead.
62 Description string `yaml:",omitempty"`
63 Published time.Time `yaml:",omitempty"`
64 LastModified *time.Time `yaml:"last_modified,omitempty"`
65 Withdrawn *time.Time `yaml:",omitempty"`
66
Julie Qiue508e322022-01-04 15:12:43 -050067 // If we are assigning a CVE ID ourselves, use CVEMetdata.ID instead.
Julie Qiu5b3cf6b2021-12-20 16:26:47 -050068 // CVE are CVE IDs for existing CVEs, if there is more than one.
69 // Use either CVE or CVEs, but not both.
70 CVEs []string `yaml:",omitempty"`
71 Credit string `yaml:",omitempty"`
72 Symbols []string `yaml:",omitempty"`
73 OS []string `yaml:",omitempty"`
74 Arch []string `yaml:",omitempty"`
75 Links Links `yaml:",omitempty"`
76
77 // CVEMetdata is used to capture CVE information when we want to assign a
78 // CVE ourselves. If a CVE already exists for an issue, use the CVE field
79 // to fill in the ID string.
80 CVEMetadata *CVEMeta `yaml:"cve_metadata,omitempty"`
81}
Jonathan Amsterdam566ba412022-01-20 08:40:49 -050082
Jonathan Amsterdamb63dc7c2022-01-20 09:40:07 -050083// Read reads a Report in YAML format from filename.
Jonathan Amsterdam566ba412022-01-20 08:40:49 -050084func Read(filename string) (_ *Report, err error) {
85 defer derrors.Wrap(&err, "report.Read(%q)", filename)
86
Jonathan Amsterdamb63dc7c2022-01-20 09:40:07 -050087 f, err := os.Open(filename)
Jonathan Amsterdam566ba412022-01-20 08:40:49 -050088 if err != nil {
89 return nil, err
90 }
Jonathan Amsterdamb63dc7c2022-01-20 09:40:07 -050091 defer f.Close()
92 d := yaml.NewDecoder(f)
93 // Require that all fields in the file are in the struct.
94 // This corresponds to v2's UnmarshalStrict.
95 d.KnownFields(true)
Jonathan Amsterdam566ba412022-01-20 08:40:49 -050096 var r Report
Jonathan Amsterdamb63dc7c2022-01-20 09:40:07 -050097 if err := d.Decode(&r); err != nil {
98 return nil, fmt.Errorf("yaml.Decode: %v", err)
Jonathan Amsterdam566ba412022-01-20 08:40:49 -050099 }
100 return &r, nil
101}
Jonathan Amsterdamb63dc7c2022-01-20 09:40:07 -0500102
103// Write writes r to filename in YAML format.
104func (r *Report) Write(filename string) (err error) {
105 f, err := os.Create(filename)
106 if err != nil {
107 return err
108 }
109 err = r.encode(f)
110 err2 := f.Close()
111 if err == nil {
112 err = err2
113 }
114 return err
115}
116
117// ToString encodes r to a YAML string.
118func (r *Report) ToString() (string, error) {
119 var b strings.Builder
120 if err := r.encode(&b); err != nil {
121 return "", err
122 }
123 return b.String(), nil
124}
125
126func (r *Report) encode(w io.Writer) error {
127 e := yaml.NewEncoder(w)
128 e.SetIndent(4)
129 return e.Encode(r)
130}