Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
93087d4
Rewrite and refactor nvd to output version information in db_specific
jess-lowe Feb 3, 2026
343d7bb
Refactor conversion to return conversion output and always create a N…
jess-lowe Feb 3, 2026
3cae059
Add csv analysis generation
jess-lowe Feb 4, 2026
c34b70f
fix lint
jess-lowe Feb 4, 2026
11493cb
l
jess-lowe Feb 4, 2026
f6e4b77
Add flag for only outputting successful conversions
jess-lowe Feb 6, 2026
2fbd16b
Use all extractable repositories for commit finding.
jess-lowe Feb 6, 2026
527abfe
Merge remote-tracking branch 'upstream/master' into feat/nvd-version-db
jess-lowe Feb 10, 2026
49f0a66
Merge branch 'master' into feat/nvd-version-db
jess-lowe Feb 10, 2026
e140287
Merge branch 'feat/nvd-version-db' of https://github.com/jess-lowe/os…
jess-lowe Feb 10, 2026
2482889
fix unnecessary logic
jess-lowe Feb 10, 2026
1fc7354
Fix some bad FindRepo logic
jess-lowe Feb 10, 2026
b915cd9
fix or/and logic
jess-lowe Feb 10, 2026
db3481e
begin to deprecate VersionInfo struct for NVD conversion
jess-lowe Feb 11, 2026
340cf11
Return ranges not affected
jess-lowe Feb 12, 2026
b26d827
Output commit ranges collected
jess-lowe Feb 13, 2026
330e73f
add qualcomm to deny list
jess-lowe Feb 13, 2026
e7ec73c
Add a flag for whether to output metrics file
jess-lowe Feb 13, 2026
b014115
More refactoring to optimise order of operations
jess-lowe Feb 15, 2026
551b686
Move GitVersionToCommit and resolveVersionToCommit to conversion comm…
jess-lowe Feb 16, 2026
f5b5e77
Fix names and make public
jess-lowe Feb 16, 2026
84e6610
remove unnecessary function and reliance on cveid
jess-lowe Feb 16, 2026
e459dda
Merge remote-tracking branch 'upstream/master' into refactor/move-more
jess-lowe Feb 16, 2026
63a35eb
Have GitVersionsToCommits output ranges instead of affected
jess-lowe Feb 16, 2026
017eaf8
fix formatting
jess-lowe Feb 16, 2026
0720af1
fix bad conversion assumptions
jess-lowe Feb 16, 2026
171ea63
-testsMerge branch 'master' into refactor/deprecate-versioninfo
jess-lowe Feb 16, 2026
1f07765
fixed some tests
jess-lowe Feb 16, 2026
ccfb2a6
create a function for duplicate code
jess-lowe Feb 16, 2026
29e5cea
Add ConductAnalysis and CpuProfile
jess-lowe Feb 16, 2026
4c27138
fix formatting
jess-lowe Feb 16, 2026
98fccb3
fix gocritic complaints
jess-lowe Feb 16, 2026
943ad4e
Merge branch 'feat/conduct-analysis' into refactor/deprecate-versioninfo
jess-lowe Feb 16, 2026
59fdd2e
Merge branch 'refactor/move-more' into refactor/deprecate-versioninfo
jess-lowe Feb 16, 2026
e9e6fba
add check for invalid cache
jess-lowe Feb 16, 2026
6afecfa
Add check for if there are only commits
jess-lowe Feb 17, 2026
2d939ee
Merge remote-tracking branch 'upstream/master' into refactor/deprecat…
jess-lowe Feb 19, 2026
7e8dfd8
fix lint
jess-lowe Feb 19, 2026
6ba77b1
fix lint
jess-lowe Feb 19, 2026
5473ea6
Merge branch 'master' into refactor/deprecate-versioninfo
jess-lowe Feb 19, 2026
b979745
clean up resolveVersionToCommit docstring
jess-lowe Mar 1, 2026
debeb96
Added docstrings and changed order of early exit
jess-lowe Mar 1, 2026
68296cb
we dont want os vulns rn
jess-lowe Mar 1, 2026
097f720
update docstring
jess-lowe Mar 1, 2026
0b8caa5
Put the check back
jess-lowe Mar 2, 2026
e6debf3
Create SetOutcome function to handle updating conversion status
jess-lowe Mar 2, 2026
8f33297
fixing missing outcomes
jess-lowe Mar 2, 2026
f0cb44d
lint
jess-lowe Mar 2, 2026
17f32e1
handle edgecases for mergetworanges
jess-lowe Mar 2, 2026
2e7045a
liiiint
jess-lowe Mar 2, 2026
3db95a6
reduce duplication
jess-lowe Mar 2, 2026
d509aa7
don't needlessly return early
jess-lowe Mar 4, 2026
78d307a
Removed function and inlined it
jess-lowe Mar 6, 2026
58f65a8
Merge branch 'master' into refactor/deprecate-versioninfo
jess-lowe Mar 6, 2026
3683aa4
lint
jess-lowe Mar 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 5 additions & 20 deletions vulnfeeds/cmd/converters/cve/nvd-cve-osv/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package main

import (
"encoding/json"
"errors"
"flag"
"fmt"
"log/slog"
Expand All @@ -29,6 +28,7 @@ var (
outDir = flag.String("out-dir", "", "Path to output results.")
outFormat = flag.String("out-format", "OSV", "Format to output {OSV,PackageInfo}")
workers = flag.Int("workers", 30, "The number of concurrent workers to use for processing CVEs.")
rejectFailed = flag.Bool("reject-failed", false, "If set, OSV records with a failed conversion outcome will not be generated.")
outputMetrics = flag.Bool("output-metrics", true, "If true, output the metrics information about the conversion")
cpuProfile = flag.String("cpuprofile", "", "Path to write cpu profile to file (default = no output)")
)
Expand Down Expand Up @@ -131,30 +131,15 @@ func processCVE(cve models.NVDCVE, vpRepoCache *cves.VPRepoCache, repoTagsCache
repos := nvd.FindRepos(cve, vpRepoCache, repoTagsCache, metrics, http.DefaultClient)
metrics.Repos = repos

var err error
var outcome models.ConversionOutcome
switch *outFormat {
case "OSV":
err = nvd.CVEToOSV(cve, repos, repoTagsCache, *outDir, metrics)
outcome = nvd.CVEToOSV(cve, repos, repoTagsCache, *outDir, metrics, *rejectFailed, *outputMetrics)
case "PackageInfo":
err = nvd.CVEToPackageInfo(cve, repos, repoTagsCache, *outDir, metrics)
outcome = nvd.CVEToPackageInfo(cve, repos, repoTagsCache, *outDir, metrics)
}
// Parse this error to determine which failure mode it was
if err != nil {
if errors.Is(err, nvd.ErrNoRanges) {
metrics.Outcome = models.NoRanges
return models.NoRanges
}
if errors.Is(err, nvd.ErrUnresolvedFix) {
metrics.Outcome = models.FixUnresolvable
return models.FixUnresolvable
}
metrics.Outcome = models.ConversionUnknown

return models.ConversionUnknown
}
metrics.Outcome = models.Successful

return models.Successful
return outcome
}

func worker(wg *sync.WaitGroup, jobs <-chan models.NVDCVE, _ string, vpRepoCache *cves.VPRepoCache, repoTagsCache *git.RepoTagsCache) {
Expand Down
154 changes: 128 additions & 26 deletions vulnfeeds/conversion/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package conversion
import (
"encoding/csv"
"encoding/json"
"errors"
"fmt"
"io/fs"
"log/slog"
Expand Down Expand Up @@ -171,7 +172,8 @@ func WriteMetricsFile(metrics *models.ConversionMetrics, metricsFile *os.File) e
return nil
}

// Examines repos and tries to convert versions to commits by treating them as Git tags.
// GitVersionsToCommits examines repos and tries to convert versions to commits by treating them as Git tags.
// Returns the resolved ranges, unresolved ranges, and successful repos involved.
func GitVersionsToCommits(versionRanges []*osvschema.Range, repos []string, metrics *models.ConversionMetrics, cache *git.RepoTagsCache) ([]*osvschema.Range, []*osvschema.Range, []string) {
var newVersionRanges []*osvschema.Range
unresolvedRanges := versionRanges
Expand All @@ -181,7 +183,9 @@ func GitVersionsToCommits(versionRanges []*osvschema.Range, repos []string, metr
if len(unresolvedRanges) == 0 {
break // All ranges have been resolved.
}

if cache.IsInvalid(repo) {
continue
}
normalizedTags, err := git.NormalizeRepoTags(repo, cache)
if err != nil {
metrics.AddNote("Failed to normalize tags - %s", repo)
Expand All @@ -207,10 +211,19 @@ func GitVersionsToCommits(versionRanges []*osvschema.Range, repos []string, metr
if introduced == "0" {
introducedCommit = "0"
} else {
introducedCommit = resolveVersionToCommit(introduced, normalizedTags)
introducedCommit, err = git.VersionToCommit(introduced, normalizedTags)
if err != nil {
metrics.AddNote("error resolving version to commit - %s - %s", introduced, err)
}
}
fixedCommit, err := git.VersionToCommit(fixed, normalizedTags)
if err != nil {
metrics.AddNote("error resolving version to commit - %s - %s", fixed, err)
}
lastAffectedCommit, err := git.VersionToCommit(lastAffected, normalizedTags)
if err != nil {
metrics.AddNote("error resolving version to commit - %s - %s", lastAffected, err)
}
fixedCommit := resolveVersionToCommit(fixed, normalizedTags)
lastAffectedCommit := resolveVersionToCommit(lastAffected, normalizedTags)

if introducedCommit != "" && (fixedCommit != "" || lastAffectedCommit != "") {
var newVR *osvschema.Range
Expand Down Expand Up @@ -240,18 +253,6 @@ func GitVersionsToCommits(versionRanges []*osvschema.Range, repos []string, metr
unresolvedRanges = stillUnresolvedRanges
}

if len(newVersionRanges) > 0 {
metrics.ResolvedRangesCount += len(newVersionRanges)
metrics.Outcome = models.Successful
}

if len(unresolvedRanges) > 0 {
metrics.UnresolvedRangesCount += len(unresolvedRanges)
if len(newVersionRanges) == 0 {
metrics.Outcome = models.NoCommitRanges
}
}

return newVersionRanges, unresolvedRanges, successfulRepos
}

Expand Down Expand Up @@ -280,16 +281,117 @@ func BuildVersionRange(intro string, lastAff string, fixed string) *osvschema.Ra
return &versionRange
}

// resolveVersionToCommit is a helper to convert a version string to a commit hash.
// It logs the outcome of the conversion attempt and returns an empty string on failure.
func resolveVersionToCommit(version string, normalizedTags map[string]git.NormalizedTag) string {
if version == "" {
return ""
// MergeTwoRanges combines two osvschema.Range objects into a single range.
// It merges the events and the DatabaseSpecific fields. If the ranges are
// not for the same repository or are of different types, it returns an error.
// When merging DatabaseSpecific fields, it handles lists, maps, and simple
// strings. If there are mismatching types for the same key, it returns an error.
func MergeTwoRanges(range1, range2 *osvschema.Range) (*osvschema.Range, error) {
// check if the ranges are the same
if range1.GetRepo() != range2.GetRepo() || range1.GetType() != range2.GetType() {
// return an error if not the case
return nil, errors.New("ranges are not the same repo or type")
}
commit, err := git.VersionToCommit(version, normalizedTags)
if err != nil {
return ""

mergedRange := &osvschema.Range{
Repo: range1.GetRepo(),
Type: range1.GetType(),
Events: append(range1.Events, range2.GetEvents()...),
}

db1 := range1.GetDatabaseSpecific()
db2 := range2.GetDatabaseSpecific()

if db1 == nil && db2 == nil {
return mergedRange, nil
}

return commit
mergedMap := make(map[string]any)

if db1 != nil {
for k, v := range db1.GetFields() {
mergedMap[k] = v.AsInterface()
}
}

if db2 != nil {
for k, v := range db2.GetFields() {
val2 := v.AsInterface()
if existing, ok := mergedMap[k]; ok {
mergedVal, err := mergeDatabaseSpecificValues(existing, val2)
if err != nil {
logger.Info("Failed to merge database specific key", "key", k, "err", err)
}
mergedMap[k] = mergedVal
} else {
mergedMap[k] = val2
}
}
}

if len(mergedMap) > 0 {
if ds, err := utility.NewStructpbFromMap(mergedMap); err == nil {
mergedRange.DatabaseSpecific = ds
} else {
logger.Warn("Failed to create DatabaseSpecific for merged range: %v", err)
}
}

return mergedRange, nil
}

// mergeDatabaseSpecificValues is a helper function that recursively merges two
// values from a DatabaseSpecific field. It handles lists (by appending), maps
// (by recursively merging keys), and simple strings (by creating a list if they
// differ). It returns an error if the types of the two values do not match.
func mergeDatabaseSpecificValues(val1, val2 any) (any, error) {
switch v1 := val1.(type) {
case []any:
if v2, ok := val2.([]any); ok {
return append(v1, v2...), nil
}

return nil, fmt.Errorf("mismatching types: %T and %T", val1, val2)
case map[string]any:
if v2, ok := val2.(map[string]any); ok {
merged := make(map[string]any)
for k, v := range v1 {
merged[k] = v
}
for k, v := range v2 {
if existing, ok := merged[k]; ok {
mergedVal, err := mergeDatabaseSpecificValues(existing, v)
if err != nil {
return nil, err
}
merged[k] = mergedVal
} else {
merged[k] = v
}
}

return merged, nil
}

return nil, fmt.Errorf("mismatching types: %T and %T", val1, val2)
case string:
if v2, ok := val2.(string); ok {
if v1 == v2 {
return v1, nil
}

return []any{v1, v2}, nil
}

return nil, fmt.Errorf("mismatching types: %T and %T", val1, val2)
default:
if fmt.Sprintf("%T", val1) != fmt.Sprintf("%T", val2) {
return nil, fmt.Errorf("mismatching types: %T and %T", val1, val2)
}
if val1 == val2 {
return val1, nil
}

return []any{val1, val2}, nil
}
}
Loading
Loading