@@ -4,7 +4,6 @@ package releases
44import (
55 "context"
66 "encoding/json"
7- "fmt"
87 "net/url"
98
109 "github.com/arangodb/go-driver/v2/arangodb"
@@ -473,15 +472,17 @@ func convertToModelsAffected(allAffected []map[string]interface{}) []models.Affe
473472 return result
474473}
475474
475+ // filepath: graphql/modules/releases/resolvers.go
476+
476477// ResolveOrgAggregatedReleases aggregates release data by organization
477- // FIXED: Removed runtime toLowerCase() conversion - org names are now stored lowercase
478+ // FIXED: Deduplicates vulnerability matches BEFORE calculating severity counts to ensure totals match
478479func ResolveOrgAggregatedReleases (db database.DBConnection , severity string , username string ) ([]interface {}, error ) {
479480 ctx := context .Background ()
480481 severityScore := util .GetSeverityScore (severity )
481482
482483 // Determine org filter based on authentication status
483- userOrgs := []string {} // Initialize as empty slice, not nil
484- filterByPublic := true // Default to public only
484+ userOrgs := []string {}
485+ filterByPublic := true
485486
486487 if username != "" {
487488 // User is authenticated - fetch their orgs from database
@@ -495,18 +496,14 @@ func ResolveOrgAggregatedReleases(db database.DBConnection, severity string, use
495496 var orgs []string
496497 _ , readErr := cursor .ReadDocument (ctx , & orgs )
497498 if readErr == nil && orgs != nil {
498- // Orgs are already stored lowercase - no conversion needed
499499 userOrgs = orgs
500500 }
501501 }
502502 }
503-
504- // If user has no orgs specified, they have global access (empty orgs = see all)
503+ // If user has no orgs specified, they have global access
505504 filterByPublic = false
506505 }
507506
508- fmt .Printf ("User=%s\n Orgs=%v\n " , username , userOrgs )
509-
510507 query := `
511508 FOR r IN release
512509 FILTER (
@@ -569,15 +566,19 @@ func ResolveOrgAggregatedReleases(db database.DBConnection, severity string, use
569566 }
570567 )
571568
572- LET uniqueInstances = (
569+ // FIX: Deduplicate (CVE+Package) matches FIRST
570+ LET uniqueMatches = (
573571 FOR match IN cveMatches
574- COLLECT cveId = match.cve_id, package = match.package
575- RETURN 1
572+ COLLECT cveId = match.cve_id, package = match.package INTO groups = match
573+ // Since cveId is the same, the severity_rating will be the same for all in the group
574+ LET severity = groups[0].severity_rating
575+ RETURN { cveId, package, severity }
576576 )
577577
578+ // FIX: Calculate severity counts from the UNIQUE list, not the raw list
578579 LET severityCounts = (
579- FOR match IN cveMatches
580- COLLECT severity = match.severity_rating WITH COUNT INTO count
580+ FOR match IN uniqueMatches
581+ COLLECT severity = match.severity WITH COUNT INTO count
581582 RETURN { severity, count }
582583 )
583584
@@ -610,8 +611,8 @@ func ResolveOrgAggregatedReleases(db database.DBConnection, severity string, use
610611 RETURN {
611612 dependency_count: LENGTH(dependencyCount),
612613 synced_endpoint_count: syncCount,
613- vulnerability_count: LENGTH(uniqueInstances ),
614- vulnerability_count_delta: prevVulnCount != null ? (LENGTH(uniqueInstances ) - prevVulnCount) : null,
614+ vulnerability_count: LENGTH(uniqueMatches ),
615+ vulnerability_count_delta: prevVulnCount != null ? (LENGTH(uniqueMatches ) - prevVulnCount) : null,
615616 max_severity: LENGTH(cveMatches) > 0 ? MAX(cveMatches[*].severity_score) : 0,
616617 scorecard_score: latest.openssf_scorecard_score,
617618 severity_counts: severityCounts
0 commit comments