-
Notifications
You must be signed in to change notification settings - Fork 54
/
secdb.go
121 lines (94 loc) · 3 KB
/
secdb.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
package advisory
import (
"encoding/json"
"errors"
"fmt"
"sort"
"github.com/wolfi-dev/wolfictl/pkg/advisory/secdb"
"github.com/wolfi-dev/wolfictl/pkg/configs"
v2 "github.com/wolfi-dev/wolfictl/pkg/configs/advisory/v2"
)
const apkURL = "{{urlprefix}}/{{reponame}}/{{arch}}/{{pkg.name}}-{{pkg.ver}}.apk"
// BuildSecurityDatabaseOptions contains the options for building a database.
type BuildSecurityDatabaseOptions struct {
AdvisoryDocIndices []*configs.Index[v2.Document]
URLPrefix string
Archs []string
Repo string
}
var (
ErrNoPackageSecurityData = errors.New("no package security data found")
ErrorPackageCollision = errors.New("found multiple advisory documents for the same package")
)
// BuildSecurityDatabase builds an Alpine-style security database from the given options.
func BuildSecurityDatabase(opts BuildSecurityDatabaseOptions) ([]byte, error) {
var packageEntries []secdb.PackageEntry
seenPackages := make(map[string]struct{})
for _, index := range opts.AdvisoryDocIndices {
var indexPackageEntries []secdb.PackageEntry
for _, doc := range index.Select().Configurations() {
if _, exists := seenPackages[doc.Package.Name]; exists {
return nil, fmt.Errorf(
"cannot process additional advisory data for package %q: %w",
doc.Package.Name,
ErrorPackageCollision,
)
}
seenPackages[doc.Package.Name] = struct{}{}
if len(doc.Advisories) == 0 {
continue
}
secfixes := make(secdb.Secfixes)
sort.Slice(doc.Advisories, func(i, j int) bool {
return doc.Advisories[i].ID < doc.Advisories[j].ID
})
for _, advisory := range doc.Advisories {
if len(advisory.Events) == 0 {
continue
}
sortedEvents := advisory.SortedEvents()
latest := sortedEvents[len(advisory.Events)-1]
addVulnToPkgVersion := func(vulnID string) {
switch latest.Type {
case v2.EventTypeFixed:
version := latest.Data.(v2.Fixed).FixedVersion
secfixes[version] = append(secfixes[version], vulnID)
case v2.EventTypeFalsePositiveDetermination:
secfixes[secdb.NAK] = append(secfixes[secdb.NAK], vulnID)
}
}
// Get vulnerability from advisory ID
vulnID := advisory.ID
addVulnToPkgVersion(vulnID)
// Get vulnerabilities from advisory aliases
for _, alias := range advisory.Aliases {
vulnID := alias
addVulnToPkgVersion(vulnID)
}
}
if len(secfixes) == 0 {
continue
}
pe := secdb.PackageEntry{
Pkg: secdb.Package{
Name: doc.Package.Name,
Secfixes: secfixes,
},
}
indexPackageEntries = append(indexPackageEntries, pe)
}
if len(indexPackageEntries) == 0 {
// Catch the unexpected case where an advisories directory contains no security data.
return nil, ErrNoPackageSecurityData
}
packageEntries = append(packageEntries, indexPackageEntries...)
}
db := secdb.Database{
APKURL: apkURL,
Archs: opts.Archs,
Repo: opts.Repo,
URLPrefix: opts.URLPrefix,
Packages: packageEntries,
}
return json.MarshalIndent(db, "", " ")
}