Skip to content
Merged
Show file tree
Hide file tree
Changes from 15 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
15102e4
Rewrite Debian conversion to make OSV files instead of PkgInfo #vc4a
jess-lowe Sep 3, 2025
b8d2774
Change debian id to include prefix and the OG ID as upstream
jess-lowe Sep 3, 2025
0a4975a
remove unused code
jess-lowe Sep 3, 2025
a5f5f30
Rename Debian CVE struct
jess-lowe Sep 3, 2025
f8294ea
these tests are testing me.
jess-lowe Sep 3, 2025
361626a
remove debian from combine-to-osv
jess-lowe Sep 3, 2025
4f4c42f
update dsa/dla/dtsa conversion to use DEBIAN- upstream
jess-lowe Sep 3, 2025
4d82cad
Merge remote-tracking branch 'upstream/master' into refactor/uncouple…
jess-lowe Sep 3, 2025
d5ce3bc
fix missing bracket and other fun things
jess-lowe Sep 4, 2025
56e04f3
Update output bucket
jess-lowe Sep 4, 2025
938f4dc
liiiiiiiiiiiiiint
jess-lowe Sep 4, 2025
bee5533
string lint thing
jess-lowe Sep 4, 2025
49355ac
fix test
jess-lowe Sep 4, 2025
0b7f388
L is for lint
jess-lowe Sep 4, 2025
da624e6
L is for last one??
jess-lowe Sep 4, 2025
7e6d368
just assign affectedVersion
jess-lowe Sep 4, 2025
6bd6a1d
Add published date
jess-lowe Sep 4, 2025
3dc23b7
fix tests
jess-lowe Sep 4, 2025
ea1c589
Update DSA conversion to include both DEBIAN-CVE and CVE-
jess-lowe Sep 5, 2025
05b5672
fix file name
jess-lowe Sep 7, 2025
a62297e
Merge remote-tracking branch 'upstream/master' into refactor/uncouple…
jess-lowe Sep 8, 2025
61e5095
Use NVD data for Published date
jess-lowe Sep 8, 2025
b524e8b
fix tests
jess-lowe Sep 8, 2025
b3b6645
update build script
jess-lowe Sep 8, 2025
9d61ab5
fix lint
jess-lowe Sep 8, 2025
617d5e4
Update output path to be the debian-osv bucket in line with DSAs and …
jess-lowe Sep 8, 2025
974229b
update cron job output buckets
jess-lowe Sep 8, 2025
ddc032b
don't write out files with no affected packages
jess-lowe Sep 8, 2025
bf73b98
fix lint
jess-lowe Sep 8, 2025
46af2f4
Update vulnfeeds/vulns/vulns.go
jess-lowe Sep 10, 2025
4820730
Rename loadTestData2
jess-lowe Sep 10, 2025
8eee496
reformat testdata
jess-lowe Sep 10, 2025
edc8a2d
make loadTestData a helper
jess-lowe Sep 10, 2025
7e9a081
fix mustRead thingy
jess-lowe Sep 10, 2025
e3e77b8
reformat test files
jess-lowe Sep 10, 2025
57b8170
Merge remote-tracking branch 'upstream/master' into refactor/uncouple…
jess-lowe Sep 16, 2025
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
1 change: 0 additions & 1 deletion vulnfeeds/cmd/combine-to-osv/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ Combine [`PackageInfo`](https://github.com/google/osv.dev/blob/2c22e9534a521c6c6
To address the generation of CVE records from multiple disparate sources (all requiring a common record prefix):

* Alpine, by [this code](../alpine)
* Debian, by [this code](../debian)
* the NVD, by [this code](../nvd-cve-osv)

## How
Expand Down
13 changes: 2 additions & 11 deletions vulnfeeds/cmd/combine-to-osv/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ const (

alpineEcosystem = "Alpine"
alpineSecurityTrackerURL = "https://security.alpinelinux.org/vuln"
debianEcosystem = "Debian"
debianSecurityTrackerURL = "https://security-tracker.debian.org/tracker"
)

var Logger utility.LoggerWrapper
Expand Down Expand Up @@ -170,14 +168,10 @@ func combineIntoOSV(loadedCves map[cves.CVEID]cves.Vulnerability, allParts map[c
}
}

addedDebianURL := false
addedAlpineURL := false
for _, pkgInfo := range allParts[cveID] {
convertedCve.AddPkgInfo(pkgInfo)
if strings.HasPrefix(pkgInfo.Ecosystem, debianEcosystem) && !addedDebianURL {
addReference(string(cveID), debianEcosystem, convertedCve)
addedDebianURL = true
} else if strings.HasPrefix(pkgInfo.Ecosystem, alpineEcosystem) && !addedAlpineURL {
if strings.HasPrefix(pkgInfo.Ecosystem, alpineEcosystem) && !addedAlpineURL {
addReference(string(cveID), alpineEcosystem, convertedCve)
addedAlpineURL = true
}
Expand Down Expand Up @@ -249,11 +243,8 @@ func loadAllCVEs(cvePath string) map[cves.CVEID]cves.Vulnerability {
// addReference adds the related security tracker URL to a given vulnerability's references
func addReference(cveID string, ecosystem string, convertedCve *vulns.Vulnerability) {
securityReference := osvschema.Reference{Type: osvschema.ReferenceAdvisory}
switch ecosystem {
case alpineEcosystem:
if ecosystem == alpineEcosystem {
securityReference.URL, _ = url.JoinPath(alpineSecurityTrackerURL, cveID)
case debianEcosystem:
securityReference.URL, _ = url.JoinPath(debianSecurityTrackerURL, cveID)
}

if securityReference.URL == "" {
Expand Down
6 changes: 3 additions & 3 deletions vulnfeeds/cmd/combine-to-osv/main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,13 +104,14 @@ func TestCombineIntoOSV(t *testing.T) {
if len(combinedOSV[cve].Affected) != len(allParts[cve]) {
t.Errorf("Affected lengths for %s do not match", cve)
}

found := false
switch cve {
case "CVE-2018-1000500":
for _, reference := range combinedOSV[cve].References {
if reference.Type == "ADVISORY" &&
reference.URL == "https://security-tracker.debian.org/tracker/CVE-2018-1000500" {
found = true
t.Errorf("Found unexpected Debian advisory URL for %s", cve)
}
}
case "CVE-2022-33745":
Expand All @@ -128,12 +129,11 @@ func TestCombineIntoOSV(t *testing.T) {
}
}
}
if !found {
if !found && cve != "CVE-2018-1000500" {
t.Errorf("%s doesn't have all expected references", cve)
}
}
}

func TestGetModifiedTime(t *testing.T) {
_, err := getModifiedTime("../../test_data/parts/debian/CVE-2016-1585.debian.json")
if err != nil {
Expand Down
4 changes: 2 additions & 2 deletions vulnfeeds/cmd/debian/debian_security_tracker.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ type Release struct {
Urgency string `json:"urgency"`
}

type CVE struct {
type DebianCVE struct {
Description string `json:"description"`
DebianBug int
Scope string `json:"scope"`
Releases map[string]Release `json:"releases"`
}

type DebianSecurityTrackerData map[string]map[string]CVE
type DebianSecurityTrackerData map[string]map[string]DebianCVE
190 changes: 102 additions & 88 deletions vulnfeeds/cmd/debian/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,21 +4,24 @@ package main
import (
"encoding/csv"
"encoding/json"
"flag"
"fmt"
"net/http"
"os"
"path"
"sort"
"strconv"
"time"

"github.com/google/osv/vulnfeeds/faulttolerant"
"github.com/google/osv/vulnfeeds/models"
"github.com/google/osv/vulnfeeds/utility"
"github.com/google/osv/vulnfeeds/vulns"
"github.com/ossf/osv-schema/bindings/go/osvschema"
)

const (
debianOutputPathDefault = "parts/debian"
debianOutputPathDefault = "debian_osv"
debianDistroInfoURL = "https://debian.pages.debian.net/distro-info-data/debian.csv"
debianSecurityTrackerURL = "https://security-tracker.debian.org/tracker/data/json"
)
Expand All @@ -30,7 +33,10 @@ func main() {
Logger, logCleanup = utility.CreateLoggerWrapper("debian-osv")
defer logCleanup()

err := os.MkdirAll(debianOutputPathDefault, 0755)
debianOutputPath := flag.String("output_path", debianOutputPathDefault, "Path to output OSV files.")
flag.Parse()

err := os.MkdirAll(*debianOutputPath, 0755)
if err != nil {
Logger.Fatalf("Can't create output path: %s", err)
}
Expand All @@ -45,14 +51,96 @@ func main() {
Logger.Fatalf("Failed to get Debian distro info data: %s", err)
}

cvePkgInfos := generateDebianSecurityTrackerOSV(debianData, debianReleaseMap)
if err = writeToOutput(cvePkgInfos); err != nil {
osvCves := generateOSVFromDebianTracker(debianData, debianReleaseMap)

if err = writeToOutput(osvCves, *debianOutputPath); err != nil {
Logger.Fatalf("Failed to write OSV output file: %s", err)
}

Logger.Infof("Debian CVE conversion succeeded.")
}

// generateOSVFromDebianTracker converts Debian Security Tracker entries to OSV format.
func generateOSVFromDebianTracker(debianData DebianSecurityTrackerData, debianReleaseMap map[string]string) map[string]*vulns.Vulnerability {
Logger.Infof("Converting Debian Security Tracker data to OSV.")
osvCves := make(map[string]*vulns.Vulnerability)

// Sorts packages to ensure results remain consistent between runs.
pkgNames := make([]string, 0, len(debianData))
for name := range debianData {
pkgNames = append(pkgNames, name)
}
sort.Strings(pkgNames)

// Sorts releases to ensure pkgInfos remain consistent between runs.
releaseNames := make([]string, 0, len(debianReleaseMap))
for k := range debianReleaseMap {
releaseNames = append(releaseNames, k)
}

sort.Slice(releaseNames, func(i, j int) bool {
vi, _ := strconv.ParseFloat(debianReleaseMap[releaseNames[i]], 64)
vj, _ := strconv.ParseFloat(debianReleaseMap[releaseNames[j]], 64)

return vi < vj
})

for _, pkgName := range pkgNames {
pkg := debianData[pkgName]
for cveID, cveData := range pkg {
v, ok := osvCves[cveID]
if !ok {
v = &vulns.Vulnerability{
Vulnerability: osvschema.Vulnerability{
ID: "DEBIAN-" + cveID,
Upstream: []string{cveID},
Modified: time.Now().UTC(),
Details: cveData.Description,
References: []osvschema.Reference{
{
Type: "ADVISORY",
URL: "https://security-tracker.debian.org/tracker/" + cveID,
},
},
},
}
osvCves[cveID] = v
}

for _, releaseName := range releaseNames {
// For reference on urgency levels, see: https://security-team.debian.org/security_tracker.html#severity-levels
release, ok := cveData.Releases[releaseName]
if !ok {
continue
}
debianVersion, ok := debianReleaseMap[releaseName]
if !ok {
continue
}

if release.Status == "resolved" && release.FixedVersion == "0" { // not affected
continue
}

pkgInfo := vulns.PackageInfo{
PkgName: pkgName,
Ecosystem: "Debian:" + debianVersion,
EcosystemSpecific: map[string]any{
"urgency": release.Urgency,
},
}

if release.Status == "resolved" {
pkgInfo.VersionInfo.AffectedVersions = append(pkgInfo.VersionInfo.AffectedVersions, models.AffectedVersion{Fixed: release.FixedVersion})
}
v.AddPkgInfo(pkgInfo)
}
}
}

return osvCves
}

// getDebianReleaseMap gets the Debian version number, excluding testing and experimental versions.
func getDebianReleaseMap() (map[string]string, error) {
releaseMap := make(map[string]string)
Expand Down Expand Up @@ -100,98 +188,24 @@ func getDebianReleaseMap() (map[string]string, error) {
return releaseMap, err
}

// updateOSVPkgInfos adds new release entries to osvPkgInfos.
func updateOSVPkgInfos(pkgName string, cveID string, releases map[string]Release, osvPkgInfos map[string][]vulns.PackageInfo, debianReleaseMap map[string]string, releaseNames []string) {
//nolint:prealloc
var pkgInfos []vulns.PackageInfo
if value, ok := osvPkgInfos[cveID]; ok {
pkgInfos = value
}

for _, releaseName := range releaseNames {
// For reference on urgency levels, see: https://security-team.debian.org/security_tracker.html#severity-levels
release, ok := releases[releaseName]
if !ok {
continue
}
debianVersion, ok := debianReleaseMap[releaseName]
if !ok {
continue
}

pkgInfo := vulns.PackageInfo{
PkgName: pkgName,
Ecosystem: "Debian:" + debianVersion,
}
pkgInfo.EcosystemSpecific = make(map[string]any)

pkgInfo.VersionInfo = models.VersionInfo{
AffectedVersions: []models.AffectedVersion{{Introduced: "0"}},
}
if release.Status == "resolved" {
if release.FixedVersion == "0" { // not affected
continue
}
pkgInfo.VersionInfo.AffectedVersions = append(pkgInfo.VersionInfo.AffectedVersions, models.AffectedVersion{Fixed: release.FixedVersion})
}
pkgInfo.EcosystemSpecific["urgency"] = release.Urgency
pkgInfos = append(pkgInfos, pkgInfo)
}
if pkgInfos != nil {
osvPkgInfos[cveID] = pkgInfos
}
}

// generateDebianSecurityTrackerOSV converts Debian Security Tracker entries to OSV PackageInfo format.
func generateDebianSecurityTrackerOSV(debianData DebianSecurityTrackerData, debianReleaseMap map[string]string) map[string][]vulns.PackageInfo {
Logger.Infof("Converting Debian Security Tracker data to OSV package infos.")
osvPkgInfos := make(map[string][]vulns.PackageInfo)

// Sorts packages to ensure results remain consistent between runs.
pkgNames := make([]string, 0, len(debianData))
for name := range debianData {
pkgNames = append(pkgNames, name)
}
sort.Strings(pkgNames)

// Sorts releases to ensure pkgInfos remain consistent between runs.
releaseNames := make([]string, 0, len(debianReleaseMap))
for k := range debianReleaseMap {
releaseNames = append(releaseNames, k)
}

sort.Slice(releaseNames, func(i, j int) bool {
vi, _ := strconv.ParseFloat(debianReleaseMap[releaseNames[i]], 64)
vj, _ := strconv.ParseFloat(debianReleaseMap[releaseNames[j]], 64)

return vi < vj
})

for _, pkgName := range pkgNames {
pkg := debianData[pkgName]
for cveID, cve := range pkg {
updateOSVPkgInfos(pkgName, cveID, cve.Releases, osvPkgInfos, debianReleaseMap, releaseNames)
}
}

return osvPkgInfos
}

func writeToOutput(cvePkgInfos map[string][]vulns.PackageInfo) error {
Logger.Infof("Writing package infos to the output.")
for cveID := range cvePkgInfos {
pkgInfos := cvePkgInfos[cveID]
file, err := os.OpenFile(path.Join(debianOutputPathDefault, cveID+".debian.json"), os.O_CREATE|os.O_RDWR, 0644)
func writeToOutput(osvCves map[string]*vulns.Vulnerability, debianOutputPath string) error {
Logger.Infof("Writing OSV files to the output.")
for cveID, osv := range osvCves {
file, err := os.OpenFile(path.Join(debianOutputPath, cveID+".json"), os.O_CREATE|os.O_RDWR|os.O_TRUNC, 0644)
if err != nil {
return err
}

encoder := json.NewEncoder(file)
encoder.SetIndent("", " ")
err = encoder.Encode(&pkgInfos)
err = encoder.Encode(osv)
closeErr := file.Close()
if err != nil {
return err
}
_ = file.Close()
if closeErr != nil {
return closeErr
}
}

return nil
Expand Down
Loading
Loading