Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
48 changes: 24 additions & 24 deletions controllers/vulndb_controller.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package controllers

import (
"encoding/json"
"fmt"
"net/url"
"strconv"
Expand Down Expand Up @@ -216,35 +215,36 @@ type ecosystemRow struct {
Count int `gorm:"count" json:"count"`
}

// return the number of affected packages by ecosystem
func (c VulnDBController) GetEcosystemDistribution(ctx shared.Context) error {
results := make([]ecosystemRow, 1024)
// return the number of vulnerabilities in affected packages per ecosystem
func (c VulnDBController) GetCVEEcosystemDistribution(ctx shared.Context) error {
cveResults := make([]ecosystemRow, 0, 1024)
maliciousPackageResults := make([]ecosystemRow, 0, 64)

// static sql to get amount of packages by ecosystem
sql := `SELECT ecosystem, COUNT(*) FROM affected_components GROUP BY ecosystem;`
err := c.affectedComponentRepository.GetDB(nil).Raw(sql).Find(&results).Error
// get the amount of CVEs in affected packages per ecosystem
cveSQL := `SELECT LOWER(b.ecosystem) as ecosystem, COUNT(*) FROM cve_affected_component a
LEFT JOIN affected_components b ON b.id = a.affected_component_id
GROUP BY LOWER(b.ecosystem);`
err := c.affectedComponentRepository.GetDB(nil).Raw(cveSQL).Find(&cveResults).Error
Comment thread
timbastin marked this conversation as resolved.
if err != nil {
return echo.NewHTTPError(500, "could not fetch data from database").WithInternal(err)
}

// since ecosystem have tags behind the : character we want to group them by their prefix
jsonResults := buildResultsJSON(results)

return ctx.String(200, jsonResults)
}

// group ecosystem by prefix ecosystem string and return the equivalent json encoding
func buildResultsJSON(rows []ecosystemRow) string {
// map to deduplicate ecosystem with different tags
aggregatedResults := make(map[string]int)
// do the same thing for malicious packages
maliciousPackagesSQL := `SELECT LOWER(b.ecosystem) as ecosystem, COUNT(*) FROM malicious_packages a
Comment thread
timbastin marked this conversation as resolved.
LEFT JOIN malicious_affected_components b ON a.id = b.malicious_package_id
GROUP BY LOWER(b.ecosystem);`
err = c.affectedComponentRepository.GetDB(nil).Raw(maliciousPackagesSQL).Find(&maliciousPackageResults).Error
Comment on lines +224 to +236

Copilot AI Feb 10, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ecosystem is nullable in both affected_components and malicious_affected_components tables, but the query groups by LOWER(b.ecosystem) without filtering NULL/empty values. This can yield a NULL/empty ecosystem bucket that later gets mapped to an empty-string key; consider adding WHERE b.ecosystem IS NOT NULL AND b.ecosystem <> '' (and similarly for the malicious packages query) to avoid returning an ambiguous "" ecosystem.

Copilot uses AI. Check for mistakes.
if err != nil {
return echo.NewHTTPError(500, "could not fetch data from database").WithInternal(err)
}

// fill the map with the value of the rows
for _, row := range rows {
before, _, _ := strings.Cut(row.Ecosystem, ":")
aggregatedResults[before] += row.Count
// group the results in a map by cutting the ecosystem identifier before the ':'
ecosystemToAmount := make(map[string]int, len(cveResults))
for _, row := range append(cveResults, maliciousPackageResults...) {
key, _, _ := strings.Cut(row.Ecosystem, ":")
ecosystemToAmount[key] += row.Count
}

// marshal to JSON with proper indentation
data, _ := json.MarshalIndent(aggregatedResults, "", config.PrettyJSONIndent)
return string(data)
// convert the result in a map and return it
return ctx.JSONPretty(200, ecosystemToAmount, config.PrettyJSONIndent)
}
2 changes: 0 additions & 2 deletions database/repositories/cve_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,6 @@ func (g *cveRepository) FindAllListPaged(tx *gorm.DB, pageInfo shared.PageInfo,
for _, f := range filter {
q = q.Where(f.SQL(), f.Value())
}
q = q.Where("cvss > 0")
q.Count(&count)

// get all cves
Expand All @@ -147,7 +146,6 @@ func (g *cveRepository) FindAllListPaged(tx *gorm.DB, pageInfo shared.PageInfo,
for _, f := range filter {
q = q.Where(f.SQL(), f.Value())
}
q = q.Where("cvss > 0")

// apply sorting
if len(sort) > 0 {
Expand Down
2 changes: 1 addition & 1 deletion router/vulndb_router.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ func NewVulnDBRouter(apiV1Router APIV1Router, vulndbController *controllers.Vuln
cveRouter.GET("/:cveID/", vulndbController.Read)
cveRouter.GET("/purl-inspect/:purl", vulndbController.PURLInspect)
cveRouter.GET("/list-ids-by-creation-date/", vulndbController.ListIDsByCreationDate)
cveRouter.GET("/affected-package-distribution/", vulndbController.GetEcosystemDistribution)
cveRouter.GET("/cve-ecosystem-distribution/", vulndbController.GetCVEEcosystemDistribution)
Comment thread
Hubtrick-Git marked this conversation as resolved.
return VulnDBRouter{
Group: cveRouter,
}
Expand Down
Loading