Skip to content

Commit e55bb38

Browse files
committed
feat: db: add transform_bellsoft
enable OSV ecosystems: * Alpaquita * BellSoft Hardened Containers Signed-off-by: Ildar Mulyukov <ildar.mulyukov@bell-sw.com>
1 parent f289de3 commit e55bb38

3 files changed

Lines changed: 265 additions & 0 deletions

File tree

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
{
2+
"affected": [
3+
{
4+
"package": {
5+
"ecosystem": "Alpaquita:23",
6+
"name": "expat",
7+
"purl": "pkg:apk/alpaquita/expat?arch=source&distro=23"
8+
},
9+
"ranges": [
10+
{
11+
"events": [
12+
{
13+
"introduced": "2.5.0-r0"
14+
},
15+
{
16+
"fixed": "2.7.2-r0"
17+
}
18+
],
19+
"type": "ECOSYSTEM"
20+
}
21+
]
22+
},
23+
{
24+
"package": {
25+
"ecosystem": "Alpaquita:25",
26+
"name": "expat",
27+
"purl": "pkg:apk/alpaquita/expat?arch=source&distro=25"
28+
},
29+
"ranges": [
30+
{
31+
"events": [
32+
{
33+
"introduced": "2.7.1-r0"
34+
},
35+
{
36+
"fixed": "2.7.2-r0"
37+
}
38+
],
39+
"type": "ECOSYSTEM"
40+
}
41+
]
42+
},
43+
{
44+
"package": {
45+
"ecosystem": "Alpaquita:stream",
46+
"name": "expat",
47+
"purl": "pkg:apk/alpaquita/expat?arch=source&distro=stream"
48+
},
49+
"ranges": [
50+
{
51+
"events": [
52+
{
53+
"introduced": "2.4.9-r0"
54+
},
55+
{
56+
"fixed": "2.7.2-r0"
57+
}
58+
],
59+
"type": "ECOSYSTEM"
60+
}
61+
]
62+
},
63+
{
64+
"package": {
65+
"ecosystem": "BellSoft Hardened Containers:23",
66+
"name": "expat",
67+
"purl": "pkg:apk/bellsoft-hardened-containers/expat?arch=source&distro=23"
68+
},
69+
"ranges": [
70+
{
71+
"events": [
72+
{
73+
"introduced": "2.5.0-r0"
74+
},
75+
{
76+
"fixed": "2.7.2-r0"
77+
}
78+
],
79+
"type": "ECOSYSTEM"
80+
}
81+
]
82+
},
83+
{
84+
"package": {
85+
"ecosystem": "BellSoft Hardened Containers:25",
86+
"name": "expat",
87+
"purl": "pkg:apk/bellsoft-hardened-containers/expat?arch=source&distro=25"
88+
},
89+
"ranges": [
90+
{
91+
"events": [
92+
{
93+
"introduced": "2.7.1-r0"
94+
},
95+
{
96+
"fixed": "2.7.2-r0"
97+
}
98+
],
99+
"type": "ECOSYSTEM"
100+
}
101+
]
102+
},
103+
{
104+
"package": {
105+
"ecosystem": "BellSoft Hardened Containers:stream",
106+
"name": "expat",
107+
"purl": "pkg:apk/bellsoft-hardened-containers/expat?arch=source&distro=stream"
108+
},
109+
"ranges": [
110+
{
111+
"events": [
112+
{
113+
"introduced": "2.4.9-r0"
114+
},
115+
{
116+
"fixed": "2.7.2-r0"
117+
}
118+
],
119+
"type": "ECOSYSTEM"
120+
}
121+
]
122+
}
123+
],
124+
"id": "BELL-CVE-2025-59375",
125+
"modified": "2025-12-23T13:05:51.859654Z",
126+
"published": "2025-09-15T11:56:23.866967Z",
127+
"references": [
128+
{
129+
"type": "ADVISORY",
130+
"url": "https://docs.bell-sw.com/security/cves/CVE-2025-59375"
131+
},
132+
{
133+
"type": "ADVISORY",
134+
"url": "https://docs.bell-sw.com/security/advisories/BELL-SA-2025-13"
135+
}
136+
],
137+
"schema_version": "1.7.4",
138+
"severity": [
139+
{
140+
"score": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H",
141+
"type": "CVSS_V3"
142+
}
143+
],
144+
"upstream": [
145+
"CVE-2025-59375"
146+
]
147+
}

grype/db/v6/build/transformers/osv/transform.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,5 +46,6 @@ type Strategy interface {
4646
// true handles the record. Add new providers by appending here.
4747
var strategies = []Strategy{
4848
almaStrategy{},
49+
bellsoftStrategy{},
4950
bitnamiStrategy{},
5051
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package osv
2+
3+
import (
4+
"fmt"
5+
"sort"
6+
"strings"
7+
8+
"github.com/google/osv-scanner/pkg/models"
9+
10+
"github.com/anchore/grype/grype/db/data"
11+
"github.com/anchore/grype/grype/db/internal/provider/unmarshal"
12+
"github.com/anchore/grype/grype/db/provider"
13+
db "github.com/anchore/grype/grype/db/v6"
14+
"github.com/anchore/grype/grype/db/v6/build/transformers"
15+
"github.com/anchore/grype/grype/db/v6/build/transformers/internal"
16+
"github.com/anchore/grype/grype/db/v6/name"
17+
"github.com/anchore/syft/syft/pkg"
18+
)
19+
20+
// bellsoftStrategy handles BELL-* records from BellSoft's vulnerability database.
21+
// BellSoft records describe *affected* version ranges of upstream components
22+
// (apache, node, spark, etc.) packaged by BellSoft.
23+
//
24+
// BellSoft-specific decisions:
25+
// - CVE refs are in `upstream` field
26+
// - Package type comes from the PURL (always present, e.g. pkg:apk/alpaquita/apache).
27+
// - Ecosystem stays as "BellSoft" — there is no underlying distro.
28+
// - No qualifiers are emitted. `database_specific.cpes` is intentionally
29+
// dropped: the bellsoft matcher never queries by CPE, the platform CPE
30+
// qualifier is a runtime no-op for application CPEs, and CPEs without
31+
// ecosystem context produce noisy matches.
32+
type bellsoftStrategy struct{}
33+
34+
func (bellsoftStrategy) Matches(id string) bool {
35+
return strings.HasPrefix(id, "BELL-")
36+
}
37+
38+
func (bellsoftStrategy) Transform(vuln unmarshal.OSVVulnerability, state provider.State) ([]data.Entry, error) {
39+
severities, err := getSeverities(vuln)
40+
if err != nil {
41+
return nil, fmt.Errorf("unable to obtain severities: %w", err)
42+
}
43+
44+
in := []any{
45+
db.VulnerabilityHandle{
46+
Name: vuln.ID,
47+
ProviderID: state.Provider,
48+
Provider: provider.Model(state),
49+
Status: db.VulnerabilityActive,
50+
ModifiedDate: &vuln.Modified,
51+
PublishedDate: &vuln.Published,
52+
BlobValue: &db.VulnerabilityBlob{
53+
ID: vuln.ID,
54+
Description: vuln.Details,
55+
References: bellsoftReferences(vuln),
56+
Aliases: vuln.Aliases,
57+
Severities: severities,
58+
},
59+
},
60+
}
61+
62+
for _, aph := range bellsoftAffectedPackages(vuln) {
63+
in = append(in, aph)
64+
}
65+
return transformers.NewEntries(in...), nil
66+
}
67+
68+
func bellsoftReferences(vuln unmarshal.OSVVulnerability) []db.Reference {
69+
var refs []db.Reference
70+
for _, ref := range vuln.References {
71+
refs = append(refs, db.Reference{
72+
URL: ref.URL,
73+
Tags: []string{string(ref.Type)},
74+
})
75+
}
76+
return refs
77+
}
78+
79+
func bellsoftAffectedPackages(vuln unmarshal.OSVVulnerability) []db.AffectedPackageHandle {
80+
if len(vuln.Affected) == 0 {
81+
return nil
82+
}
83+
var aphs []db.AffectedPackageHandle
84+
for _, affected := range vuln.Affected {
85+
var ranges []db.Range
86+
for _, r := range affected.Ranges {
87+
ranges = append(ranges, getGrypeRangesFromRange(r, bellsoftRangeType(r.Type))...)
88+
}
89+
aphs = append(aphs, db.AffectedPackageHandle{
90+
Package: bellsoftPackage(affected.Package),
91+
BlobValue: &db.PackageBlob{
92+
CVEs: vuln.Aliases, // FIXME: should be `vuln.Upstream`
93+
Ranges: ranges,
94+
},
95+
})
96+
}
97+
sort.Sort(internal.ByAffectedPackage(aphs))
98+
return aphs
99+
}
100+
101+
func bellsoftPackage(p models.Package) *db.Package {
102+
pkgType := pkg.TypeFromPURL(p.Purl)
103+
return &db.Package{
104+
Ecosystem: string(p.Ecosystem),
105+
Name: name.Normalize(p.Name, pkgType),
106+
}
107+
}
108+
109+
// bellsoftRangeType maps an OSV range type to the grype version-format string
110+
// for BellSoft records. SEMVER ranges describe apk-flavored semver
111+
// (separate version comparator); other OSV types fall through to the default.
112+
func bellsoftRangeType(t models.RangeType) string {
113+
if t == models.RangeSemVer {
114+
return "apk"
115+
}
116+
return defaultRangeType(t)
117+
}

0 commit comments

Comments
 (0)