-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathstore.go
300 lines (259 loc) · 10.8 KB
/
store.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
// Package store supports permanent data storage for the vuln worker.
package store
import (
"context"
"errors"
"fmt"
"time"
"github.com/go-git/go-git/v5/plumbing/object"
"github.com/khulnasoft-lab/go-vulndb/internal/cveschema"
"github.com/khulnasoft-lab/go-vulndb/internal/ghsa"
)
// A CVERecord contains information about a CVE.
type CVERecord struct {
// ID is the CVE ID, which is the same as the filename base. E.g. "CVE-2020-0034".
ID string
// Path is the path to the CVE file in the repo.
Path string
// Blobhash is the hash of the CVE's blob in repo, for quick change detection.
BlobHash string
// CommitHash is the commit of the cvelist repo from which this information came.
CommitHash string
// CommitTime is the time of the above commit.
// If zero, it has not been populated.
CommitTime time.Time
// CVEState is the value of the metadata.STATE field.
CVEState string
// TriageState is the state of our triage processing on the CVE.
TriageState TriageState
// TriageStateReason is an explanation of TriageState.
TriageStateReason string
// Module is the Go module path that might be affected.
Module string
// Package is the Go package path that might be affected.
Package string
// CVE is a copy of the CVE, for the NeedsIssue triage state.
CVE *cveschema.CVE
// ReferenceURLs is a list of the URLs in the CVE references,
// for the FalsePositive triage state.
ReferenceURLs []string
// IssueReference is a reference to the GitHub issue that was filed.
// E.g. golang/vulndb#12345.
// Set only after a GitHub issue has been successfully created.
IssueReference string
// IssueCreatedAt is the time when the issue was created.
// Set only after a GitHub issue has been successfully created.
IssueCreatedAt time.Time
// History holds previous states of a CVERecord,
// from most to least recent.
History []*CVERecordSnapshot
}
func (r *CVERecord) GetID() string { return r.ID }
func (r *CVERecord) GetUnit() string { return r.Module }
func (r *CVERecord) GetIssueReference() string { return r.IssueReference }
func (r *CVERecord) GetIssueCreatedAt() time.Time { return r.IssueCreatedAt }
// Validate returns an error if the CVERecord is not valid.
func (r *CVERecord) Validate() error {
if r.ID == "" {
return errors.New("need ID")
}
if r.Path == "" {
return errors.New("need Path")
}
if r.BlobHash == "" {
return errors.New("need BlobHash")
}
if r.CommitHash == "" {
return errors.New("need CommitHash")
}
if r.CommitTime.IsZero() {
return errors.New("need CommitTime")
}
return r.TriageState.Validate()
}
// TriageState is the state of our work on the CVE or GHSA.
// It is implemented as a string rather than an int so that stored values are
// immune to renumbering.
type TriageState string
const (
// No action is needed on the CVE or GHSA (perhaps because it is rejected, reserved or invalid).
TriageStateNoActionNeeded TriageState = "NoActionNeeded"
// The CVE needs to have an issue created.
TriageStateNeedsIssue TriageState = "NeedsIssue"
// An issue has been created in the issue tracker.
// The IssueReference and IssueCreatedAt fields have more information.
TriageStateIssueCreated TriageState = "IssueCreated"
// This vulnerability has already been handled under an alias (i.e., a CVE
// or GHSA that refers to the same vulnerability).
TriageStateAlias TriageState = "Alias"
// The CVE state was changed after the CVE was created.
TriageStateUpdatedSinceIssueCreation TriageState = "UpdatedSinceIssueCreation"
// Although the triager might think this CVE is relevant to Go, it is not.
TriageStateFalsePositive TriageState = "FalsePositive"
// There is already an entry in the Go vuln DB that covers this CVE.
TriageStateHasVuln TriageState = "HasVuln"
)
// Validate returns an error if the TriageState is not one of the above values.
func (s TriageState) Validate() error {
switch s {
case TriageStateNoActionNeeded, TriageStateNeedsIssue, TriageStateIssueCreated, TriageStateAlias, TriageStateUpdatedSinceIssueCreation, TriageStateFalsePositive, TriageStateHasVuln:
return nil
default:
return fmt.Errorf("bad TriageState %q", s)
}
}
// NewCVERecord creates a CVERecord from a CVE, its path and its blob hash.
func NewCVERecord(cve *cveschema.CVE, path, blobHash string, commit *object.Commit) *CVERecord {
return &CVERecord{
ID: cve.ID,
CVEState: cve.State,
Path: path,
BlobHash: blobHash,
CommitHash: commit.Hash.String(),
CommitTime: commit.Committer.When.In(time.UTC),
}
}
// CVERecordSnapshot holds a previous state of a CVERecord.
// The fields mean the same as those of CVERecord.
type CVERecordSnapshot struct {
CommitHash string
CVEState string
TriageState TriageState
TriageStateReason string
}
func (r *CVERecord) Snapshot() *CVERecordSnapshot {
return &CVERecordSnapshot{
CommitHash: r.CommitHash,
CVEState: r.CVEState,
TriageState: r.TriageState,
TriageStateReason: r.TriageStateReason,
}
}
// A CommitUpdateRecord describes a single update operation, which reconciles
// a commit in the CVE list repo with the DB state.
type CommitUpdateRecord struct {
// The ID of this record in the DB. Needed to modify the record.
ID string
// When the update started and completed. If EndedAt is zero,
// the update is in progress (or it crashed).
StartedAt, EndedAt time.Time
// The repo commit hash that this update is working on.
CommitHash string
// The time the commit occurred.
CommitTime time.Time
// The total number of CVEs being processed in this update.
NumTotal int
// The number currently processed. When this equals NumTotal, the
// update is done.
NumProcessed int
// The number of CVEs added to the DB.
NumAdded int
// The number of CVEs modified.
NumModified int
// The error that stopped the update.
Error string
// The last time this record was updated.
UpdatedAt time.Time `firestore:",serverTimestamp"`
}
// A GHSARecord holds information about a GitHub security advisory.
type GHSARecord struct {
// GHSA is the advisory.
GHSA *ghsa.SecurityAdvisory
// TriageState is the state of our triage processing on the CVE.
TriageState TriageState
// TriageStateReason is an explanation of TriageState.
TriageStateReason string
// IssueReference is a reference to the GitHub issue that was filed.
// E.g. golang/vulndb#12345.
// Set only after a GitHub issue has been successfully created.
IssueReference string
// IssueCreatedAt is the time when the issue was created.
// Set only after a GitHub issue has been successfully created.
IssueCreatedAt time.Time
}
func (r *GHSARecord) GetID() string { return r.GHSA.ID }
func (r *GHSARecord) GetUnit() string { return r.GHSA.Vulns[0].Package }
func (r *GHSARecord) GetIssueReference() string { return r.IssueReference }
func (r *GHSARecord) GetIssueCreatedAt() time.Time { return r.IssueCreatedAt }
// A ModuleScanRecord holds information about a vulnerability scan of a module.
type ModuleScanRecord struct {
Path string
Version string
DBTime time.Time // last-modified time of the vuln DB
Error string // if non-empty, error while scanning
VulnIDs []string
FinishedAt time.Time // when the scan completed (successfully or not)
}
// Validate returns an error if the ModuleScanRecord is not valid.
func (r *ModuleScanRecord) Validate() error {
if r.Path == "" {
return errors.New("need Path")
}
if r.Version == "" {
return errors.New("need Version")
}
if r.DBTime.IsZero() {
return errors.New("need DBTime")
}
if r.FinishedAt.IsZero() {
return errors.New("need FinishedAt")
}
return nil
}
// A Store is a storage system for the CVE database.
type Store interface {
// CreateCommitUpdateRecord creates a new CommitUpdateRecord. It should be called at the start
// of an update. On successful return, the CommitUpdateRecord's ID field will be
// set to a new, unique ID.
CreateCommitUpdateRecord(context.Context, *CommitUpdateRecord) error
// SetCommitUpdateRecord modifies the CommitUpdateRecord. Use the same record passed to
// CreateCommitUpdateRecord, because it will have the correct ID.
SetCommitUpdateRecord(context.Context, *CommitUpdateRecord) error
// ListCommitUpdateRecords returns some of the CommitUpdateRecords in the store, from most to
// least recent.
ListCommitUpdateRecords(ctx context.Context, limit int) ([]*CommitUpdateRecord, error)
// GetCVERecord returns the CVERecord with the given id. If not found, it returns (nil, nil).
GetCVERecord(ctx context.Context, id string) (*CVERecord, error)
// ListCVERecordsWithTriageState returns all CVERecords with the given triage state,
// ordered by ID.
ListCVERecordsWithTriageState(ctx context.Context, ts TriageState) ([]*CVERecord, error)
// GetDirectoryHash returns the hash for the tree object corresponding to dir.
// If dir isn't found, it succeeds with the empty string.
GetDirectoryHash(ctx context.Context, dir string) (string, error)
// SetDirectoryHash sets the hash for the given directory.
SetDirectoryHash(ctx context.Context, dir, hash string) error
// CreateModuleScanRecord adds a ModuleScanRecord to the DB.
CreateModuleScanRecord(context.Context, *ModuleScanRecord) error
// GetModuleScanRecord returns the most recent ModuleScanRecord matching the
// given module path, version and DB time. If not found, it returns (nil,
// nil).
GetModuleScanRecord(ctx context.Context, path, version string, dbTime time.Time) (*ModuleScanRecord, error)
// ListModuleScanRecords returns some of the ModuleScanRecords in the store
// from most to least recent. If limit is zero, all records are returned.
ListModuleScanRecords(ctx context.Context, limit int) ([]*ModuleScanRecord, error)
// RunTransaction runs the function in a transaction.
RunTransaction(context.Context, func(context.Context, Transaction) error) error
}
// Transaction supports store operations that run inside a transaction.
type Transaction interface {
// CreateCVERecord creates a new CVERecord. It is an error if one with the same ID
// already exists.
CreateCVERecord(*CVERecord) error
// SetCVERecord sets the CVE record in the database. It is
// an error if no such record exists.
SetCVERecord(r *CVERecord) error
// GetCVERecords retrieves CVERecords for all CVE IDs between startID and
// endID, inclusive.
GetCVERecords(startID, endID string) ([]*CVERecord, error)
// CreateGHSARecord creates a new GHSARecord. It is an error if one with the same ID
// already exists.
CreateGHSARecord(*GHSARecord) error
// SetGHSARecord sets the GHSA record in the database. It is
// an error if no such record exists.
SetGHSARecord(*GHSARecord) error
// GetGHSARecord returns a single GHSARecord by GHSA ID.
// If not found, it returns (nil, nil).
GetGHSARecord(id string) (*GHSARecord, error)
// GetGHSARecords returns all the GHSARecords in the database.
GetGHSARecords() ([]*GHSARecord, error)
}