|
| 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