Skip to content

Commit 8501898

Browse files
committed
ROX-28353: use NVD 2.0 JSON feeds (#2015)
1 parent 64fe581 commit 8501898

File tree

9 files changed

+830
-371
lines changed

9 files changed

+830
-371
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ jobs:
162162
contains(github.event.pull_request.labels.*.name, 'generate-dumps-on-pr')
163163
env:
164164
NVD_API_KEY: ${{ secrets.NVD_API_KEY }}
165-
ROX_LEGACY_NVD_LOADER: true
165+
ROX_NVD_FEED_LOADER: true
166166
runs-on: ubuntu-latest
167167
needs:
168168
- pre-build-updater

pkg/env/list.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ var (
4141
// MaxGrpcConcurrentStreams configures the maximum number of HTTP/2 streams to use with gRPC
4242
MaxGrpcConcurrentStreams = RegisterIntegerSetting("ROX_GRPC_MAX_CONCURRENT_STREAMS", DefaultMaxGrpcConcurrentStreams)
4343

44-
// LegacyNVDLoader when true will cause the loader to pull NVD data using
45-
// the NVD Legacy Data Feeds, if false will pull from the NVD 2.0 API.
46-
LegacyNVDLoader = RegisterBooleanSetting("ROX_LEGACY_NVD_LOADER", false)
44+
// NVDFeedLoader when true will cause the loader to pull NVD data using
45+
// the NVD 2.0 Data Feeds. If false, the loader will pull from the NVD 2.0 API.
46+
NVDFeedLoader = RegisterBooleanSetting("ROX_NVD_FEED_LOADER", false)
4747

4848
// RHLineage when true will cause all parent layers (a.k.a lineage) to be considered when
4949
// storing scan results for RHEL image layers.

pkg/vulnloader/nvdloader/convert.go

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ const (
1414
jsonTimeFormat = "2006-01-02T15:04Z"
1515
)
1616

17-
func toJSON(vulns []*apischema.CVEAPIJSON20DefCVEItem) ([]*jsonschema.NVDCVEFeedJSON10DefCVEItem, error) {
17+
func toJSON10(vulns []*apischema.CVEAPIJSON20DefCVEItem) ([]*jsonschema.NVDCVEFeedJSON10DefCVEItem, error) {
1818
if vulns == nil {
1919
return nil, nil
2020
}
@@ -31,6 +31,11 @@ func toJSON(vulns []*apischema.CVEAPIJSON20DefCVEItem) ([]*jsonschema.NVDCVEFeed
3131
continue
3232
}
3333

34+
// Ignore rejected vulnerabilities.
35+
if strings.EqualFold(vuln.CVE.VulnStatus, "Rejected") {
36+
continue
37+
}
38+
3439
cve := vuln.CVE
3540

3641
modifiedTime, err := toTime(cve.LastModified)
@@ -152,14 +157,18 @@ func toBaseMetricV2(metrics []*apischema.CVEAPIJSON20CVSSV2) *jsonschema.NVDCVEF
152157
}
153158

154159
func toBaseMetricV3(metrics30 []*apischema.CVEAPIJSON20CVSSV30, metrics31 []*apischema.CVEAPIJSON20CVSSV31) *jsonschema.NVDCVEFeedJSON10DefImpactBaseMetricV3 {
155-
switch {
156-
case len(metrics31) != 0:
157-
return toBaseMetricV31(metrics31)
158-
case len(metrics30) != 0:
159-
return toBaseMetricV30(metrics30)
160-
default:
161-
return nil
160+
// Prefer CVSS 3.1.
161+
baseMetric := toBaseMetricV31(metrics31)
162+
if baseMetric != nil {
163+
return baseMetric
162164
}
165+
166+
baseMetric = toBaseMetricV30(metrics30)
167+
if baseMetric != nil {
168+
return baseMetric
169+
}
170+
171+
return nil
163172
}
164173

165174
func toBaseMetricV31(metrics []*apischema.CVEAPIJSON20CVSSV31) *jsonschema.NVDCVEFeedJSON10DefImpactBaseMetricV3 {
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
package nvdloader
2+
3+
import (
4+
"os"
5+
"testing"
6+
7+
jsonschema "github.com/facebookincubator/nvdtools/cvefeed/nvd/schema"
8+
"github.com/stretchr/testify/assert"
9+
"github.com/stretchr/testify/require"
10+
)
11+
12+
func TestToJSON10(t *testing.T) {
13+
f, err := os.Open("testdata/nvdcve-2.0-2025.json")
14+
require.NoError(t, err)
15+
t.Cleanup(func() {
16+
require.NoError(t, f.Close())
17+
})
18+
19+
cveAPI, err := parseReader(f)
20+
require.NoError(t, err)
21+
22+
cveFeed, err := toJSON10(cveAPI.Vulnerabilities)
23+
assert.NoError(t, err)
24+
25+
expected := []*jsonschema.NVDCVEFeedJSON10DefCVEItem{
26+
{
27+
CVE: &jsonschema.CVEJSON40{
28+
CVEDataMeta: &jsonschema.CVEJSON40CVEDataMeta{
29+
ID: "CVE-2025-0168",
30+
},
31+
Description: &jsonschema.CVEJSON40Description{
32+
DescriptionData: []*jsonschema.CVEJSON40LangString{
33+
{
34+
Lang: "en",
35+
Value: "A vulnerability classified as critical has been found in code-projects Job Recruitment 1.0. This affects an unknown part of the file /_parse/_feedback_system.php. The manipulation of the argument person leads to sql injection. It is possible to initiate the attack remotely. The exploit has been disclosed to the public and may be used.",
36+
},
37+
},
38+
},
39+
},
40+
Configurations: &jsonschema.NVDCVEFeedJSON10DefConfigurations{
41+
Nodes: []*jsonschema.NVDCVEFeedJSON10DefNode{
42+
{
43+
CPEMatch: []*jsonschema.NVDCVEFeedJSON10DefCPEMatch{
44+
{
45+
Cpe23Uri: "cpe:2.3:a:anisha:job_recruitment:1.0:*:*:*:*:*:*:*",
46+
Vulnerable: true,
47+
},
48+
},
49+
Operator: "OR",
50+
},
51+
},
52+
},
53+
Impact: &jsonschema.NVDCVEFeedJSON10DefImpact{
54+
BaseMetricV3: &jsonschema.NVDCVEFeedJSON10DefImpactBaseMetricV3{
55+
CVSSV3: &jsonschema.CVSSV30{
56+
AttackComplexity: "LOW",
57+
AttackVector: "NETWORK",
58+
AvailabilityImpact: "NONE",
59+
BaseScore: 7.5,
60+
BaseSeverity: "HIGH",
61+
ConfidentialityImpact: "HIGH",
62+
IntegrityImpact: "NONE",
63+
PrivilegesRequired: "NONE",
64+
Scope: "UNCHANGED",
65+
UserInteraction: "NONE",
66+
VectorString: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N",
67+
Version: "3.1",
68+
},
69+
ExploitabilityScore: 3.9,
70+
ImpactScore: 3.6,
71+
},
72+
},
73+
LastModifiedDate: "2025-02-25T21:26Z",
74+
PublishedDate: "2025-01-01T14:15Z",
75+
},
76+
{
77+
CVE: &jsonschema.CVEJSON40{
78+
CVEDataMeta: &jsonschema.CVEJSON40CVEDataMeta{
79+
ID: "CVE-2025-22144",
80+
},
81+
Description: &jsonschema.CVEJSON40Description{
82+
DescriptionData: []*jsonschema.CVEJSON40LangString{
83+
{
84+
Lang: "en",
85+
Value: "NamelessMC is a free, easy to use & powerful website software for Minecraft servers. A user with admincp.core.emails or admincp.users.edit permissions can validate users and an attacker can reset their password. When the account is successfully approved by email the reset code is NULL, but when the account is manually validated by a user with admincp.core.emails or admincp.users.edit permissions then the reset_code will no longer be NULL but empty. An attacker can request http://localhost/nameless/index.php?route=/forgot_password/&c= and reset the password. As a result an attacker may compromise another users password and take over their account. This issue has been addressed in release version 2.1.3 and all users are advised to upgrade. There are no known workarounds for this vulnerability.",
86+
},
87+
},
88+
},
89+
},
90+
Configurations: &jsonschema.NVDCVEFeedJSON10DefConfigurations{
91+
Nodes: []*jsonschema.NVDCVEFeedJSON10DefNode{
92+
{
93+
CPEMatch: []*jsonschema.NVDCVEFeedJSON10DefCPEMatch{
94+
{
95+
Cpe23Uri: "cpe:2.3:a:namelessmc:nameless:*:*:*:*:*:*:*:*",
96+
VersionEndExcluding: "2.1.3",
97+
Vulnerable: true,
98+
},
99+
},
100+
Operator: "OR",
101+
},
102+
},
103+
},
104+
Impact: &jsonschema.NVDCVEFeedJSON10DefImpact{
105+
BaseMetricV3: &jsonschema.NVDCVEFeedJSON10DefImpactBaseMetricV3{
106+
CVSSV3: &jsonschema.CVSSV30{
107+
AttackComplexity: "LOW",
108+
AttackVector: "NETWORK",
109+
AvailabilityImpact: "HIGH",
110+
BaseScore: 9.8,
111+
BaseSeverity: "CRITICAL",
112+
ConfidentialityImpact: "HIGH",
113+
IntegrityImpact: "HIGH",
114+
PrivilegesRequired: "NONE",
115+
Scope: "UNCHANGED",
116+
UserInteraction: "NONE",
117+
VectorString: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H",
118+
Version: "3.1",
119+
},
120+
ExploitabilityScore: 3.9,
121+
ImpactScore: 5.9,
122+
},
123+
},
124+
LastModifiedDate: "2025-05-13T15:42Z",
125+
PublishedDate: "2025-01-13T20:15Z",
126+
},
127+
}
128+
129+
assert.ElementsMatch(t, expected, cveFeed)
130+
}

0 commit comments

Comments
 (0)