diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml index abcbf4496..5f4b2436f 100644 --- a/.github/workflows/ci.yaml +++ b/.github/workflows/ci.yaml @@ -162,7 +162,7 @@ jobs: contains(github.event.pull_request.labels.*.name, 'generate-dumps-on-pr') env: NVD_API_KEY: ${{ secrets.NVD_API_KEY }} - ROX_LEGACY_NVD_LOADER: true + ROX_NVD_FEED_LOADER: true runs-on: ubuntu-latest needs: - pre-build-updater diff --git a/cmd/clair/main.go b/cmd/clair/main.go index 58cf533bd..edc252d50 100644 --- a/cmd/clair/main.go +++ b/cmd/clair/main.go @@ -113,7 +113,8 @@ func Boot(config *Config, slimMode bool) { go func() { defer wg.Add(-1) var err error - db, err = database.OpenWithRetries(config.Database, true, 30, 10*time.Second) + // Wait for the DB to be ready: 10 minutes. + db, err = database.OpenWithRetries(config.Database, true, 60, 10*time.Second) if err != nil { log.WithError(err).Fatal("Failed to open database despite multiple retries...") } diff --git a/e2etests/testcase_test.go b/e2etests/testcase_test.go index 6291daff5..68aa264be 100644 --- a/e2etests/testcase_test.go +++ b/e2etests/testcase_test.go @@ -2289,7 +2289,7 @@ var testCases = []testCase{ VersionFormat: "rpm", Version: "2.9.1-4.el8.x86_64", AddedBy: "sha256:35ad9b4fba1fa6b00a6f266303348dc0cf9a7c341616e800c2738030c0f64167", - FixedBy: "2.9.1-9.el8", + FixedBy: "2.9.1-10.el8_10", Vulnerabilities: []apiV1.Vulnerability{ { Name: "RHSA-2020:4952", @@ -3595,7 +3595,7 @@ For more details about the security issue(s), including the impact, a CVSS score }, { AddedBy: "sha256:2412e60e610160d090f7e974a208c6ffd26b2d530361b7c9aa8967e160ac7996", - FixedBy: "2:8.2.2637-21.el9", + FixedBy: "2:8.2.2637-22.el9_6", Name: "vim-minimal", NamespaceName: "rhel:9", Version: "2:8.2.2637-16.el9_0.2.x86_64", @@ -3838,8 +3838,8 @@ Applications using RegexRequestMatcher with '.' in the regular expression are po FixedBy: "3.0.2-0ubuntu1.7", }, }, - FixedBy: "3.0.2-0ubuntu1.16", - // This image installs the openssl pacakge in the second layer; + FixedBy: "3.0.2-0ubuntu1.18", + // This image installs the openssl package in the second layer; // however, the first layer already installed libssl3 whose source package is openssl. // Therefore, we claim openssl was installed in the first layer. AddedBy: "sha256:301a8b74f71f85f3a31e9c7e7fedd5b001ead5bcf895bc2911c1d260e06bd987", @@ -4028,7 +4028,7 @@ All OpenShift Container Platform 4.10 users are advised to upgrade to these upda Name: "tomcat", VersionFormat: component.JavaSourceType.String(), Version: "9.0.59", - FixedBy: "9.0.90", + FixedBy: "9.0.107", Location: "tomcat-embed-core-9.0.59.jar", Vulnerabilities: []apiV1.Vulnerability{ { @@ -4056,7 +4056,7 @@ All OpenShift Container Platform 4.10 users are advised to upgrade to these upda }, { Name: "CVE-2023-28708", - Description: "\nWhen using the RemoteIpFilter with requests received from a reverse proxy via HTTP that include the X-Forwarded-Proto header set to https, session cookies created by Apache Tomcat 11.0.0-M1 to 11.0.0.-M2, 10.1.0-M1 to 10.1.5, 9.0.0-M1 to 9.0.71 and 8.5.0 to 8.5.85 did not\u00a0include the secure attribute. This could result in the user agent\u00a0transmitting the session cookie over an insecure channel.\n\n\n\n\n\n\n\n", + Description: "When using the RemoteIpFilter with requests received from a reverse proxy via HTTP that include the X-Forwarded-Proto header set to https, session cookies created by Apache Tomcat 11.0.0-M1 to 11.0.0.-M2, 10.1.0-M1 to 10.1.5, 9.0.0-M1 to 9.0.71 and 8.5.0 to 8.5.85 did not\u00a0include the secure attribute. This could result in the user agent\u00a0transmitting the session cookie over an insecure channel.\n\nOlder, EOL versions may also be affected.", Link: "https://nvd.nist.gov/vuln/detail/CVE-2023-28708", Severity: "Moderate", FixedBy: "9.0.72", @@ -4318,7 +4318,7 @@ All OpenShift Container Platform 4.10 users are advised to upgrade to these upda }, }, AddedBy: "sha256:661ff4d9561e3fd050929ee5097067c34bafc523ee60f5294a37fd08056a73ca", - FixedBy: "3.1.4-r5", + FixedBy: "3.1.7-r0", }, }, }, @@ -4354,7 +4354,34 @@ All OpenShift Container Platform 4.10 users are advised to upgrade to these upda NamespaceName: "alpine:v3.20", VersionFormat: "apk", Version: "3.3.0-r2", - AddedBy: "sha256:d25f557d7f31bf7acfac935859b5153da41d13c41f2b468d16f729a5b883634f", + Vulnerabilities: []apiV1.Vulnerability{ + { + Name: "CVE-2024-6119", + NamespaceName: "alpine:v3.20", + Description: "Issue summary: Applications performing certificate name checks (e.g., TLS\nclients checking server certificates) may attempt to read an invalid memory\naddress resulting in abnormal termination of the application process.\n\nImpact summary: Abnormal termination of an application can a cause a denial of\nservice.\n\nApplications performing certificate name checks (e.g., TLS clients checking\nserver certificates) may attempt to read an invalid memory address when\ncomparing the expected name with an `otherName` subject alternative name of an\nX.509 certificate. This may result in an exception that terminates the\napplication program.\n\nNote that basic certificate chain validation (signatures, dates, ...) is not\naffected, the denial of service can occur only when the application also\nspecifies an expected DNS name, Email address or IP address.\n\nTLS servers rarely solicit client certificates, and even when they do, they\ngenerally don't perform a name check against a reference identifier (expected\nidentity), but rather extract the presented identity after checking the\ncertificate chain. So TLS servers are generally not affected and the severity\nof the issue is Moderate.\n\nThe FIPS modules in 3.3, 3.2, 3.1 and 3.0 are not affected by this issue.", + Link: "https://www.cve.org/CVERecord?id=CVE-2024-6119", + Severity: "Important", + FixedBy: "3.3.2-r0", + Metadata: map[string]interface{}{ + "NVD": map[string]interface{}{ + "CVSSv2": map[string]interface{}{ + "ExploitabilityScore": 0.0, + "ImpactScore": 0.0, + "Score": 0.0, + "Vectors": "", + }, + "CVSSv3": map[string]interface{}{ + "ExploitabilityScore": 3.9, + "ImpactScore": 3.6, + "Score": 7.5, + "Vectors": "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H", + }, + }, + }, + }, + }, + AddedBy: "sha256:d25f557d7f31bf7acfac935859b5153da41d13c41f2b468d16f729a5b883634f", + FixedBy: "3.3.2-r0", }, }, }, @@ -4399,30 +4426,6 @@ All OpenShift Container Platform 4.10 users are advised to upgrade to these upda }, }, }, - { - Name: "CVE-2024-47177", - NamespaceName: "rhel:8", - Description: "DOCUMENTATION: A security flaw was found in OpenPrinting CUPS. The `FoomaticRIPCommandLine` directive in a PPD file will allow arbitrary commands to be executed when a print job is sent to the affected printing device. An attacker who could provide a malicious PPD file for a new print device or modify a PPD file of an existing device could execute arbitrary commands. \n STATEMENT: Red Hat rates the severity of this CVE as Moderate due to the restrictions of the unprivileged ‘lp’ user which the resulting commands would be run as. Additionally, creating and modifying printer definitions is restricted to the privileged user groups sys, root, and wheel by default.\n MITIGATION: See the security bulletin for a detailed mitigation procedure.", - Link: "https://access.redhat.com/security/cve/CVE-2024-47177", - Severity: "Moderate", - FixedBy: "", - Metadata: map[string]interface{}{ - "Red Hat": map[string]interface{}{ - "CVSSv2": map[string]interface{}{ - "ExploitabilityScore": 0.0, - "ImpactScore": 0.0, - "Score": 0.0, - "Vectors": "", - }, - "CVSSv3": map[string]interface{}{ - "ExploitabilityScore": 0.6, - "ImpactScore": 5.5, - "Score": 6.1, - "Vectors": "CVSS:3.1/AV:L/AC:L/PR:H/UI:R/S:U/C:H/I:H/A:L", - }, - }, - }, - }, }, }, { @@ -4794,7 +4797,7 @@ All OpenShift Container Platform 4.10 users are advised to upgrade to these upda NamespaceName: "rhel:8", VersionFormat: "rpm", Version: "1:17.0.11.0.9-2.el8.x86_64", - FixedBy: "1:17.0.14.0.7-3.el8", + FixedBy: "1:17.0.16.0.8-2.el8", AddedBy: "sha256:06c7a3d491f551a56296ccb9bee8a68c83776991e73a9005e8b5ebb533002097", }, }, @@ -4813,7 +4816,7 @@ All OpenShift Container Platform 4.10 users are advised to upgrade to these upda NamespaceName: "rhel:8", VersionFormat: "rpm", Version: "1:17.0.13.0.11-3.el8.x86_64", - FixedBy: "1:17.0.14.0.7-3.el8", + FixedBy: "1:17.0.16.0.8-2.el8", AddedBy: "sha256:2f7b9495af5ddc85b0be7ca9411fddb54f37999ea73b03cbf1115dd0c5bd4f95", }, }, diff --git a/pkg/env/list.go b/pkg/env/list.go index 6a9719a89..bcfc107a8 100644 --- a/pkg/env/list.go +++ b/pkg/env/list.go @@ -41,9 +41,9 @@ var ( // MaxGrpcConcurrentStreams configures the maximum number of HTTP/2 streams to use with gRPC MaxGrpcConcurrentStreams = RegisterIntegerSetting("ROX_GRPC_MAX_CONCURRENT_STREAMS", DefaultMaxGrpcConcurrentStreams) - // LegacyNVDLoader when true will cause the loader to pull NVD data using - // the NVD Legacy Data Feeds, if false will pull from the NVD 2.0 API. - LegacyNVDLoader = RegisterBooleanSetting("ROX_LEGACY_NVD_LOADER", false) + // NVDFeedLoader when true will cause the loader to pull NVD data using + // the NVD 2.0 Data Feeds. If false, the loader will pull from the NVD 2.0 API. + NVDFeedLoader = RegisterBooleanSetting("ROX_NVD_FEED_LOADER", false) // RHLineage when true will cause all parent layers (a.k.a lineage) to be considered when // storing scan results for RHEL image layers. diff --git a/pkg/vulnloader/nvdloader/convert.go b/pkg/vulnloader/nvdloader/convert.go index 05bc52c20..4c7aa3432 100644 --- a/pkg/vulnloader/nvdloader/convert.go +++ b/pkg/vulnloader/nvdloader/convert.go @@ -14,7 +14,7 @@ const ( jsonTimeFormat = "2006-01-02T15:04Z" ) -func toJSON(vulns []*apischema.CVEAPIJSON20DefCVEItem) ([]*jsonschema.NVDCVEFeedJSON10DefCVEItem, error) { +func toJSON10(vulns []*apischema.CVEAPIJSON20DefCVEItem) ([]*jsonschema.NVDCVEFeedJSON10DefCVEItem, error) { if vulns == nil { return nil, nil } @@ -31,6 +31,11 @@ func toJSON(vulns []*apischema.CVEAPIJSON20DefCVEItem) ([]*jsonschema.NVDCVEFeed continue } + // Ignore rejected vulnerabilities. + if strings.EqualFold(vuln.CVE.VulnStatus, "Rejected") { + continue + } + cve := vuln.CVE modifiedTime, err := toTime(cve.LastModified) @@ -152,14 +157,18 @@ func toBaseMetricV2(metrics []*apischema.CVEAPIJSON20CVSSV2) *jsonschema.NVDCVEF } func toBaseMetricV3(metrics30 []*apischema.CVEAPIJSON20CVSSV30, metrics31 []*apischema.CVEAPIJSON20CVSSV31) *jsonschema.NVDCVEFeedJSON10DefImpactBaseMetricV3 { - switch { - case len(metrics31) != 0: - return toBaseMetricV31(metrics31) - case len(metrics30) != 0: - return toBaseMetricV30(metrics30) - default: - return nil + // Prefer CVSS 3.1. + baseMetric := toBaseMetricV31(metrics31) + if baseMetric != nil { + return baseMetric } + + baseMetric = toBaseMetricV30(metrics30) + if baseMetric != nil { + return baseMetric + } + + return nil } func toBaseMetricV31(metrics []*apischema.CVEAPIJSON20CVSSV31) *jsonschema.NVDCVEFeedJSON10DefImpactBaseMetricV3 { diff --git a/pkg/vulnloader/nvdloader/convert_test.go b/pkg/vulnloader/nvdloader/convert_test.go new file mode 100644 index 000000000..fc81b4bbb --- /dev/null +++ b/pkg/vulnloader/nvdloader/convert_test.go @@ -0,0 +1,130 @@ +package nvdloader + +import ( + "os" + "testing" + + jsonschema "github.com/facebookincubator/nvdtools/cvefeed/nvd/schema" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestToJSON10(t *testing.T) { + f, err := os.Open("testdata/nvdcve-2.0-2025.json") + require.NoError(t, err) + t.Cleanup(func() { + require.NoError(t, f.Close()) + }) + + cveAPI, err := parseReader(f) + require.NoError(t, err) + + cveFeed, err := toJSON10(cveAPI.Vulnerabilities) + assert.NoError(t, err) + + expected := []*jsonschema.NVDCVEFeedJSON10DefCVEItem{ + { + CVE: &jsonschema.CVEJSON40{ + CVEDataMeta: &jsonschema.CVEJSON40CVEDataMeta{ + ID: "CVE-2025-0168", + }, + Description: &jsonschema.CVEJSON40Description{ + DescriptionData: []*jsonschema.CVEJSON40LangString{ + { + Lang: "en", + 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.", + }, + }, + }, + }, + Configurations: &jsonschema.NVDCVEFeedJSON10DefConfigurations{ + Nodes: []*jsonschema.NVDCVEFeedJSON10DefNode{ + { + CPEMatch: []*jsonschema.NVDCVEFeedJSON10DefCPEMatch{ + { + Cpe23Uri: "cpe:2.3:a:anisha:job_recruitment:1.0:*:*:*:*:*:*:*", + Vulnerable: true, + }, + }, + Operator: "OR", + }, + }, + }, + Impact: &jsonschema.NVDCVEFeedJSON10DefImpact{ + BaseMetricV3: &jsonschema.NVDCVEFeedJSON10DefImpactBaseMetricV3{ + CVSSV3: &jsonschema.CVSSV30{ + AttackComplexity: "LOW", + AttackVector: "NETWORK", + AvailabilityImpact: "NONE", + BaseScore: 7.5, + BaseSeverity: "HIGH", + ConfidentialityImpact: "HIGH", + IntegrityImpact: "NONE", + PrivilegesRequired: "NONE", + Scope: "UNCHANGED", + UserInteraction: "NONE", + VectorString: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", + Version: "3.1", + }, + ExploitabilityScore: 3.9, + ImpactScore: 3.6, + }, + }, + LastModifiedDate: "2025-02-25T21:26Z", + PublishedDate: "2025-01-01T14:15Z", + }, + { + CVE: &jsonschema.CVEJSON40{ + CVEDataMeta: &jsonschema.CVEJSON40CVEDataMeta{ + ID: "CVE-2025-22144", + }, + Description: &jsonschema.CVEJSON40Description{ + DescriptionData: []*jsonschema.CVEJSON40LangString{ + { + Lang: "en", + 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.", + }, + }, + }, + }, + Configurations: &jsonschema.NVDCVEFeedJSON10DefConfigurations{ + Nodes: []*jsonschema.NVDCVEFeedJSON10DefNode{ + { + CPEMatch: []*jsonschema.NVDCVEFeedJSON10DefCPEMatch{ + { + Cpe23Uri: "cpe:2.3:a:namelessmc:nameless:*:*:*:*:*:*:*:*", + VersionEndExcluding: "2.1.3", + Vulnerable: true, + }, + }, + Operator: "OR", + }, + }, + }, + Impact: &jsonschema.NVDCVEFeedJSON10DefImpact{ + BaseMetricV3: &jsonschema.NVDCVEFeedJSON10DefImpactBaseMetricV3{ + CVSSV3: &jsonschema.CVSSV30{ + AttackComplexity: "LOW", + AttackVector: "NETWORK", + AvailabilityImpact: "HIGH", + BaseScore: 9.8, + BaseSeverity: "CRITICAL", + ConfidentialityImpact: "HIGH", + IntegrityImpact: "HIGH", + PrivilegesRequired: "NONE", + Scope: "UNCHANGED", + UserInteraction: "NONE", + VectorString: "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + Version: "3.1", + }, + ExploitabilityScore: 3.9, + ImpactScore: 5.9, + }, + }, + LastModifiedDate: "2025-05-13T15:42Z", + PublishedDate: "2025-01-13T20:15Z", + }, + } + + assert.ElementsMatch(t, expected, cveFeed) +} diff --git a/pkg/vulnloader/nvdloader/loader.go b/pkg/vulnloader/nvdloader/loader.go index 8038a7d52..5a74ce038 100644 --- a/pkg/vulnloader/nvdloader/loader.go +++ b/pkg/vulnloader/nvdloader/loader.go @@ -1,266 +1,15 @@ package nvdloader import ( - "encoding/json" - "fmt" - "net/http" - "os" - "path/filepath" - "time" - - apischema "github.com/facebookincubator/nvdtools/cveapi/nvd/schema" - jsonschema "github.com/facebookincubator/nvdtools/cvefeed/nvd/schema" - "github.com/facebookincubator/nvdtools/wfn" - log "github.com/sirupsen/logrus" - "github.com/stackrox/rox/pkg/httputil/proxy" - "github.com/stackrox/rox/pkg/utils" "github.com/stackrox/scanner/pkg/env" "github.com/stackrox/scanner/pkg/vulndump" "github.com/stackrox/scanner/pkg/vulnloader" ) -const urlFmt = `https://services.nvd.nist.gov/rest/json/cves/2.0?noRejected&startIndex=%d` - -var client = http.Client{ - Timeout: 5 * time.Minute, - Transport: proxy.RoundTripper(), -} - func init() { - if env.LegacyNVDLoader.Enabled() { - vulnloader.RegisterLoader(vulndump.NVDDirName, &legacyLoader{}) + if env.NVDFeedLoader.Enabled() { + vulnloader.RegisterLoader(vulndump.NVDDirName, &feedLoader{}) } else { - vulnloader.RegisterLoader(vulndump.NVDDirName, &loader{}) - } -} - -var _ vulnloader.Loader = (*loader)(nil) - -type loader struct{} - -// DownloadFeedsToPath downloads the NVD feeds to the given path. -// If this function is successful, it will fill the directory with -// one json file for each year of NVD data. -func (l *loader) DownloadFeedsToPath(outputDir string) error { - log.Info("Downloading NVD data using NVD 2.0 API") - - // Fetch NVD enrichment data from curated repos - enrichments, err := Fetch() - if err != nil { - return fmt.Errorf("could not fetch NVD enrichment sources: %w", err) - } - - nvdDir := filepath.Join(outputDir, vulndump.NVDDirName) - if err := os.MkdirAll(nvdDir, 0755); err != nil { - return fmt.Errorf("creating subdir for %s: %w", vulndump.NVDDirName, err) + vulnloader.RegisterLoader(vulndump.NVDDirName, &apiLoader{}) } - - var fileNo, totalVulns int - - // Explicitly set startIdx to parallel how this is all done within the loop below. - startIdx := 0 - apiResp, err := query(fmt.Sprintf(urlFmt, startIdx)) - if err != nil { - return err - } - var i int - // Buffer to store vulns until they are written to a file. - cveItems := make([]*jsonschema.NVDCVEFeedJSON10DefCVEItem, 0, 20_000) - for apiResp.ResultsPerPage != 0 { - vulns, err := toJSON(apiResp.Vulnerabilities) - if err != nil { - return fmt.Errorf("failed to convert API vulns to JSON: %w", err) - } - - if len(vulns) != 0 { - cveItems = append(cveItems, vulns...) - - i++ - // Write to disk every ~20,000 vulnerabilities. - if i == 10 { - i = 0 - - enrichCVEItems(&cveItems, enrichments) - - feed := &jsonschema.NVDCVEFeedJSON10{ - CVEItems: cveItems, - } - if err := writeFile(filepath.Join(nvdDir, fmt.Sprintf("%d.json", fileNo)), feed); err != nil { - return fmt.Errorf("writing to file: %w", err) - } - - fileNo++ - totalVulns += len(cveItems) - log.Infof("Loaded %d NVD vulnerabilities", totalVulns) - // Reduce, reuse, and recycle. - cveItems = cveItems[:0] - } - } - - // Rudimentary rate-limiting. - // NVD limits users without an API key to roughly one call every 6 seconds. - // With an API key, it is roughly one call every 0.6 seconds. - // We'll play it safe and do one call every 3 seconds. - // As of writing there are ~216,000 vulnerabilities, so this whole process should take ~5.4 minutes. - time.Sleep(3 * time.Second) - - startIdx += apiResp.ResultsPerPage - apiResp, err = query(fmt.Sprintf(urlFmt, startIdx)) - if err != nil { - return err - } - } - - // Write the remaining vulnerabilities. - if len(cveItems) != 0 { - enrichCVEItems(&cveItems, enrichments) - - feed := &jsonschema.NVDCVEFeedJSON10{ - CVEItems: cveItems, - } - if err := writeFile(filepath.Join(nvdDir, fmt.Sprintf("%d.json", fileNo)), feed); err != nil { - return fmt.Errorf("writing to file: %w", err) - } - - totalVulns += len(cveItems) - log.Infof("Loaded %d NVD vulnerabilities", totalVulns) - } - - return nil -} - -func query(url string) (*apischema.CVEAPIJSON20, error) { - log.Debugf("Querying %s", url) - req, err := http.NewRequest(http.MethodGet, url, nil) - if err != nil { - return nil, fmt.Errorf("creating HTTP request: %w", err) - } - req.Header.Set("apiKey", os.Getenv("NVD_API_KEY")) - - apiResp, err := queryWithBackoff(req) - if err != nil { - return nil, err - } - - return apiResp, nil -} - -func queryWithBackoff(req *http.Request) (*apischema.CVEAPIJSON20, error) { - var ( - apiResp *apischema.CVEAPIJSON20 - err error - ) - for i := 1; i <= 5; i++ { - var resp *http.Response - resp, err = tryQuery(req) - if err == nil { - apiResp, err = parseResponse(resp) - if err == nil { - break - } - } - log.Warnf("Failed query attempt %d for %s: %v", i, req.URL.String(), err) - // Wait some multiple of 3 seconds before next attempt. - time.Sleep(time.Duration(3*i) * time.Second) - } - - return apiResp, err -} - -func tryQuery(req *http.Request) (*http.Response, error) { - resp, err := client.Do(req) - if err != nil { - return nil, fmt.Errorf("fetching NVD API results: %w", err) - } - - log.Debugf("Queried %s with status code %d", req.URL.String(), resp.StatusCode) - if resp.StatusCode != 200 { - utils.IgnoreError(resp.Body.Close) - return nil, fmt.Errorf("unexpected status code when querying %s: %d", req.URL.String(), resp.StatusCode) - } - - return resp, nil -} - -func parseResponse(resp *http.Response) (*apischema.CVEAPIJSON20, error) { - defer utils.IgnoreError(resp.Body.Close) - - apiResp := new(apischema.CVEAPIJSON20) - if err := json.NewDecoder(resp.Body).Decode(apiResp); err != nil { - return nil, fmt.Errorf("decoding API response: %w", err) - } - - return apiResp, nil -} - -func enrichCVEItems(cveItems *[]*jsonschema.NVDCVEFeedJSON10DefCVEItem, enrichments map[string]*FileFormatWrapper) { - if cveItems == nil { - return - } - - cves := (*cveItems)[:0] - for _, item := range *cveItems { - if _, ok := manuallyEnrichedVulns[item.CVE.CVEDataMeta.ID]; ok { - log.Warnf("Skipping vuln %s because it is being manually enriched", item.CVE.CVEDataMeta.ID) - continue - } - - for _, node := range item.Configurations.Nodes { - removeInvalidCPEs(node) - } - - if enrichedEntry, ok := enrichments[item.CVE.CVEDataMeta.ID]; ok { - // Add the CPE matches instead of removing for backwards compatibility purposes - item.Configurations.Nodes = append(item.Configurations.Nodes, &jsonschema.NVDCVEFeedJSON10DefNode{ - CPEMatch: enrichedEntry.AffectedPackages, - Operator: "OR", - }) - item.LastModifiedDate = enrichedEntry.LastUpdated - } - cves = append(cves, item) - } - - for _, item := range manuallyEnrichedVulns { - cves = append(cves, item) - } - - *cveItems = cves -} - -func removeInvalidCPEs(item *jsonschema.NVDCVEFeedJSON10DefNode) { - cpeMatches := item.CPEMatch[:0] - for _, cpeMatch := range item.CPEMatch { - if cpeMatch.Cpe23Uri == "" { - cpeMatches = append(cpeMatches, cpeMatch) - continue - } - attr, err := wfn.UnbindFmtString(cpeMatch.Cpe23Uri) - if err != nil { - log.Errorf("error parsing %+v", item) - continue - } - if attr.Product == wfn.Any { - log.Warnf("Filtering out CPE: %+v", attr) - continue - } - cpeMatches = append(cpeMatches, cpeMatch) - } - item.CPEMatch = cpeMatches - for _, child := range item.Children { - removeInvalidCPEs(child) - } -} - -func writeFile(path string, feed *jsonschema.NVDCVEFeedJSON10) error { - outF, err := os.Create(path) - if err != nil { - return fmt.Errorf("failed to create file %s: %w", outF.Name(), err) - } - defer utils.IgnoreError(outF.Close) - - if err := json.NewEncoder(outF).Encode(feed); err != nil { - return fmt.Errorf("could not encode JSON for %s: %w", outF.Name(), err) - } - - return nil } diff --git a/pkg/vulnloader/nvdloader/loader_api.go b/pkg/vulnloader/nvdloader/loader_api.go new file mode 100644 index 000000000..4fc032aff --- /dev/null +++ b/pkg/vulnloader/nvdloader/loader_api.go @@ -0,0 +1,262 @@ +package nvdloader + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "os" + "path/filepath" + "time" + + apischema "github.com/facebookincubator/nvdtools/cveapi/nvd/schema" + jsonschema "github.com/facebookincubator/nvdtools/cvefeed/nvd/schema" + "github.com/facebookincubator/nvdtools/wfn" + log "github.com/sirupsen/logrus" + "github.com/stackrox/rox/pkg/httputil/proxy" + "github.com/stackrox/rox/pkg/utils" + "github.com/stackrox/scanner/pkg/vulndump" + "github.com/stackrox/scanner/pkg/vulnloader" +) + +const urlFmt = `https://services.nvd.nist.gov/rest/json/cves/2.0?noRejected&startIndex=%d` + +var client = http.Client{ + Timeout: 5 * time.Minute, + Transport: proxy.RoundTripper(), +} + +var _ vulnloader.Loader = (*apiLoader)(nil) + +type apiLoader struct{} + +// DownloadFeedsToPath downloads the NVD 2.0 API data to the given path. +// If this function is successful, it will fill the directory with +// one JSON file for each year of NVD data. +func (l *apiLoader) DownloadFeedsToPath(outputDir string) error { + log.Info("Downloading NVD data using NVD 2.0 API") + + // Fetch NVD enrichment data from curated repos + enrichments, err := Fetch() + if err != nil { + return fmt.Errorf("could not fetch NVD enrichment sources: %w", err) + } + + nvdDir := filepath.Join(outputDir, vulndump.NVDDirName) + if err := os.MkdirAll(nvdDir, 0755); err != nil { + return fmt.Errorf("creating subdir for %s: %w", vulndump.NVDDirName, err) + } + + var fileNo, totalVulns int + + // Explicitly set startIdx to parallel how this is all done within the loop below. + startIdx := 0 + apiResp, err := query(fmt.Sprintf(urlFmt, startIdx)) + if err != nil { + return err + } + var i int + // Buffer to store vulns until they are written to a file. + cveItems := make([]*jsonschema.NVDCVEFeedJSON10DefCVEItem, 0, 20_000) + for apiResp.ResultsPerPage != 0 { + vulns, err := toJSON10(apiResp.Vulnerabilities) + if err != nil { + return fmt.Errorf("failed to convert API vulns to JSON: %w", err) + } + + if len(vulns) != 0 { + cveItems = append(cveItems, vulns...) + + i++ + // Write to disk every ~20,000 vulnerabilities. + if i == 10 { + i = 0 + + enrichCVEItems(&cveItems, enrichments) + + feed := &jsonschema.NVDCVEFeedJSON10{ + CVEItems: cveItems, + } + if err := writeFile(filepath.Join(nvdDir, fmt.Sprintf("%d.json", fileNo)), feed); err != nil { + return fmt.Errorf("writing to file: %w", err) + } + + fileNo++ + totalVulns += len(cveItems) + log.Infof("Loaded %d NVD vulnerabilities", totalVulns) + // Reduce, reuse, and recycle. + cveItems = cveItems[:0] + } + } + + // Rudimentary rate-limiting. + // NVD limits users without an API key to roughly one call every 6 seconds. + // With an API key, it is roughly one call every 0.6 seconds. + // We'll play it safe and do one call every 3 seconds. + // As of writing there are ~216,000 vulnerabilities, so this whole process should take ~5.4 minutes. + time.Sleep(3 * time.Second) + + startIdx += apiResp.ResultsPerPage + apiResp, err = query(fmt.Sprintf(urlFmt, startIdx)) + if err != nil { + return err + } + } + + // Write the remaining vulnerabilities. + if len(cveItems) != 0 { + enrichCVEItems(&cveItems, enrichments) + + feed := &jsonschema.NVDCVEFeedJSON10{ + CVEItems: cveItems, + } + if err := writeFile(filepath.Join(nvdDir, fmt.Sprintf("%d.json", fileNo)), feed); err != nil { + return fmt.Errorf("writing to file: %w", err) + } + + totalVulns += len(cveItems) + log.Infof("Loaded %d NVD vulnerabilities", totalVulns) + } + + return nil +} + +func query(url string) (*apischema.CVEAPIJSON20, error) { + log.Debugf("Querying %s", url) + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("creating HTTP request: %w", err) + } + req.Header.Set("apiKey", os.Getenv("NVD_API_KEY")) + + apiResp, err := queryWithBackoff(req) + if err != nil { + return nil, err + } + + return apiResp, nil +} + +func queryWithBackoff(req *http.Request) (*apischema.CVEAPIJSON20, error) { + var ( + apiResp *apischema.CVEAPIJSON20 + err error + ) + for i := 1; i <= 5; i++ { + var resp *http.Response + resp, err = tryQuery(req) + if err == nil { + apiResp, err = parseResponse(resp) + if err == nil { + break + } + } + log.Warnf("Failed query attempt %d for %s: %v", i, req.URL.String(), err) + // Wait some multiple of 3 seconds before next attempt. + time.Sleep(time.Duration(3*i) * time.Second) + } + + return apiResp, err +} + +func tryQuery(req *http.Request) (*http.Response, error) { + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("fetching NVD API results: %w", err) + } + + log.Debugf("Queried %s with status code %d", req.URL.String(), resp.StatusCode) + if resp.StatusCode != 200 { + utils.IgnoreError(resp.Body.Close) + return nil, fmt.Errorf("unexpected status code when querying %s: %d", req.URL.String(), resp.StatusCode) + } + + return resp, nil +} + +func parseResponse(resp *http.Response) (*apischema.CVEAPIJSON20, error) { + defer utils.IgnoreError(resp.Body.Close) + + return parseReader(resp.Body) +} + +func parseReader(r io.Reader) (*apischema.CVEAPIJSON20, error) { + apiJSON := new(apischema.CVEAPIJSON20) + if err := json.NewDecoder(r).Decode(apiJSON); err != nil { + return nil, fmt.Errorf("decoding API response: %w", err) + } + + return apiJSON, nil +} + +func enrichCVEItems(cveItems *[]*jsonschema.NVDCVEFeedJSON10DefCVEItem, enrichments map[string]*FileFormatWrapper) { + if cveItems == nil { + return + } + + cves := (*cveItems)[:0] + for _, item := range *cveItems { + if _, ok := manuallyEnrichedVulns[item.CVE.CVEDataMeta.ID]; ok { + log.Warnf("Skipping vuln %s because it is being manually enriched", item.CVE.CVEDataMeta.ID) + continue + } + + for _, node := range item.Configurations.Nodes { + removeInvalidCPEs(node) + } + + if enrichedEntry, ok := enrichments[item.CVE.CVEDataMeta.ID]; ok { + // Add the CPE matches instead of removing for backwards compatibility purposes + item.Configurations.Nodes = append(item.Configurations.Nodes, &jsonschema.NVDCVEFeedJSON10DefNode{ + CPEMatch: enrichedEntry.AffectedPackages, + Operator: "OR", + }) + item.LastModifiedDate = enrichedEntry.LastUpdated + } + cves = append(cves, item) + } + + for _, item := range manuallyEnrichedVulns { + cves = append(cves, item) + } + + *cveItems = cves +} + +func removeInvalidCPEs(item *jsonschema.NVDCVEFeedJSON10DefNode) { + cpeMatches := item.CPEMatch[:0] + for _, cpeMatch := range item.CPEMatch { + if cpeMatch.Cpe23Uri == "" { + cpeMatches = append(cpeMatches, cpeMatch) + continue + } + attr, err := wfn.UnbindFmtString(cpeMatch.Cpe23Uri) + if err != nil { + log.Errorf("error parsing %+v", item) + continue + } + if attr.Product == wfn.Any { + log.Warnf("Filtering out CPE: %+v", attr) + continue + } + cpeMatches = append(cpeMatches, cpeMatch) + } + item.CPEMatch = cpeMatches + for _, child := range item.Children { + removeInvalidCPEs(child) + } +} + +func writeFile(path string, feed *jsonschema.NVDCVEFeedJSON10) error { + outF, err := os.Create(path) + if err != nil { + return fmt.Errorf("failed to create file %s: %w", outF.Name(), err) + } + defer utils.IgnoreError(outF.Close) + + if err := json.NewEncoder(outF).Encode(feed); err != nil { + return fmt.Errorf("could not encode JSON for %s: %w", outF.Name(), err) + } + + return nil +} diff --git a/pkg/vulnloader/nvdloader/loader_feed.go b/pkg/vulnloader/nvdloader/loader_feed.go new file mode 100644 index 000000000..19398c9a1 --- /dev/null +++ b/pkg/vulnloader/nvdloader/loader_feed.go @@ -0,0 +1,83 @@ +package nvdloader + +import ( + "compress/gzip" + "encoding/json" + "fmt" + "os" + "path/filepath" + "time" + + apischema "github.com/facebookincubator/nvdtools/cveapi/nvd/schema" + jsonschema "github.com/facebookincubator/nvdtools/cvefeed/nvd/schema" + "github.com/pkg/errors" + log "github.com/sirupsen/logrus" + "github.com/stackrox/rox/pkg/utils" + "github.com/stackrox/scanner/pkg/vulndump" + "github.com/stackrox/scanner/pkg/vulnloader" +) + +var _ vulnloader.Loader = (*feedLoader)(nil) + +type feedLoader struct{} + +// DownloadFeedsToPath downloads the NVD JSON 2.0 feeds to the given path. +// If this function is successful, it will fill the directory with +// one JSON file for each year of NVD data. +func (l *feedLoader) DownloadFeedsToPath(outputDir string) error { + log.Info("Downloading NVD data using 2.0 Data Feed") + + // Fetch NVD enrichment data from curated repos + enrichments, err := Fetch() + if err != nil { + return errors.Wrap(err, "could not fetch NVD enrichment sources") + } + + nvdDir := filepath.Join(outputDir, vulndump.NVDDirName) + if err := os.MkdirAll(nvdDir, 0755); err != nil { + return errors.Wrapf(err, "creating subdir for %s", vulndump.NVDDirName) + } + endYear := time.Now().Year() + for year := 2002; year <= endYear; year++ { + if err := l.downloadFeedForYear(enrichments, nvdDir, year); err != nil { + return err + } + } + return nil +} + +func (l *feedLoader) downloadFeedForYear(enrichments map[string]*FileFormatWrapper, outputDir string, year int) error { + url := fmt.Sprintf("https://nvd.nist.gov/feeds/json/cve/2.0/nvdcve-2.0-%d.json.gz", year) + resp, err := client.Get(url) + if err != nil { + return errors.Wrapf(err, "failed to download feed for year %d", year) + } + defer utils.IgnoreError(resp.Body.Close) + + // Un-gzip it. + gr, err := gzip.NewReader(resp.Body) + if err != nil { + return errors.Wrapf(err, "couldn't read resp body for year %d", year) + } + + apiFeed := new(apischema.CVEAPIJSON20) + if err := json.NewDecoder(gr).Decode(apiFeed); err != nil { + return errors.Wrapf(err, "decoding feed response") + } + + cveItems, err := toJSON10(apiFeed.Vulnerabilities) + if err != nil { + return fmt.Errorf("failed to convert feed vulns to JSON: %w", err) + } + + enrichCVEItems(&cveItems, enrichments) + + feed := &jsonschema.NVDCVEFeedJSON10{ + CVEItems: cveItems, + } + if err := writeFile(filepath.Join(outputDir, fmt.Sprintf("%d.json", year)), feed); err != nil { + return errors.Wrapf(err, "writing to file") + } + + return nil +} diff --git a/pkg/vulnloader/nvdloader/loader_legacy.go b/pkg/vulnloader/nvdloader/loader_legacy.go deleted file mode 100644 index 4fd0bde49..000000000 --- a/pkg/vulnloader/nvdloader/loader_legacy.go +++ /dev/null @@ -1,105 +0,0 @@ -package nvdloader - -import ( - "compress/gzip" - "encoding/json" - "fmt" - "os" - "path/filepath" - "time" - - "github.com/facebookincubator/nvdtools/cvefeed/nvd/schema" - "github.com/pkg/errors" - log "github.com/sirupsen/logrus" - "github.com/stackrox/rox/pkg/utils" - "github.com/stackrox/scanner/pkg/vulndump" - "github.com/stackrox/scanner/pkg/vulnloader" -) - -var _ vulnloader.Loader = (*legacyLoader)(nil) - -type legacyLoader struct{} - -// DownloadFeedsToPath downloads the NVD feeds to the given path. -// If this function is successful, it will fill the directory with -// one json file for each year of NVD data. -func (l *legacyLoader) DownloadFeedsToPath(outputDir string) error { - log.Info("Downloading NVD data using Legacy Data Feed") - - // Fetch NVD enrichment data from curated repos - enrichmentMap, err := Fetch() - if err != nil { - return errors.Wrap(err, "could not fetch NVD enrichment sources") - } - - nvdDir := filepath.Join(outputDir, vulndump.NVDDirName) - if err := os.MkdirAll(nvdDir, 0755); err != nil { - return errors.Wrapf(err, "creating subdir for %s", vulndump.NVDDirName) - } - endYear := time.Now().Year() - for year := 2002; year <= endYear; year++ { - if err := l.downloadFeedForYear(enrichmentMap, nvdDir, year); err != nil { - return err - } - } - return nil -} - -func (l *legacyLoader) downloadFeedForYear(enrichmentMap map[string]*FileFormatWrapper, outputDir string, year int) error { - url := l.jsonFeedURLForYear(year) - resp, err := client.Get(url) - if err != nil { - return errors.Wrapf(err, "failed to download feed for year %d", year) - } - defer utils.IgnoreError(resp.Body.Close) - // Un-gzip it. - gr, err := gzip.NewReader(resp.Body) - if err != nil { - return errors.Wrapf(err, "couldn't read resp body for year %d", year) - } - - // Strip out tabs and newlines for size savings - dump, err := LoadJSONFileFromReader(gr) - if err != nil { - return errors.Wrapf(err, "could not decode json for year %d", year) - } - - cveItems := dump.CVEItems[:0] - for _, item := range dump.CVEItems { - if _, ok := manuallyEnrichedVulns[item.CVE.CVEDataMeta.ID]; ok { - log.Warnf("Skipping vuln %s because it is being manually enriched", item.CVE.CVEDataMeta.ID) - continue - } - for _, node := range item.Configurations.Nodes { - removeInvalidCPEs(node) - } - if enrichedEntry, ok := enrichmentMap[item.CVE.CVEDataMeta.ID]; ok { - // Add the CPE matches instead of removing for backwards compatibility purposes - item.Configurations.Nodes = append(item.Configurations.Nodes, &schema.NVDCVEFeedJSON10DefNode{ - CPEMatch: enrichedEntry.AffectedPackages, - Operator: "OR", - }) - item.LastModifiedDate = enrichedEntry.LastUpdated - } - cveItems = append(cveItems, item) - } - for _, item := range manuallyEnrichedVulns { - cveItems = append(cveItems, item) - } - dump.CVEItems = cveItems - - outF, err := os.Create(filepath.Join(outputDir, fmt.Sprintf("%d.json", year))) - if err != nil { - return errors.Wrap(err, "failed to create file") - } - defer utils.IgnoreError(outF.Close) - - if err := json.NewEncoder(outF).Encode(&dump); err != nil { - return errors.Wrapf(err, "could not encode json map for year %d", year) - } - return nil -} - -func (l *legacyLoader) jsonFeedURLForYear(year int) string { - return fmt.Sprintf("https://nvd.nist.gov/feeds/json/cve/1.1/nvdcve-1.1-%d.json.gz", year) -} diff --git a/pkg/vulnloader/nvdloader/testdata/nvdcve-2.0-2025.json b/pkg/vulnloader/nvdloader/testdata/nvdcve-2.0-2025.json new file mode 100644 index 000000000..e4451a88b --- /dev/null +++ b/pkg/vulnloader/nvdloader/testdata/nvdcve-2.0-2025.json @@ -0,0 +1,331 @@ +{ + "resultsPerPage" : 4, + "startIndex" : 0, + "totalResults" : 4, + "format" : "NVD_CVE", + "version" : "2.0", + "timestamp" : "2025-08-06T03:00:00.9285242", + "vulnerabilities" : [ { + "cve" : { + "id" : "CVE-2025-0168", + "sourceIdentifier" : "cna@vuldb.com", + "published" : "2025-01-01T14:15:23.590", + "lastModified" : "2025-02-25T21:26:07.113", + "vulnStatus" : "Analyzed", + "cveTags" : [ ], + "descriptions" : [ { + "lang" : "en", + "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." + }, { + "lang" : "es", + "value" : "Se ha encontrado una vulnerabilidad clasificada como crítica en code-projects Job Recruitment 1.0. Afecta a una parte desconocida del archivo /_parse/_feedback_system.php. La manipulación del argumento person conduce a la inyección SQL. Es posible iniciar el ataque de forma remota. La vulnerabilidad se ha revelado al público y puede utilizarse." + } ], + "metrics" : { + "cvssMetricV40" : [ { + "source" : "cna@vuldb.com", + "type" : "Secondary", + "cvssData" : { + "version" : "4.0", + "vectorString" : "CVSS:4.0/AV:N/AC:L/AT:N/PR:L/UI:N/VC:L/VI:L/VA:L/SC:N/SI:N/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X", + "baseScore" : 5.3, + "baseSeverity" : "MEDIUM", + "attackVector" : "NETWORK", + "attackComplexity" : "LOW", + "attackRequirements" : "NONE", + "privilegesRequired" : "LOW", + "userInteraction" : "NONE", + "vulnConfidentialityImpact" : "LOW", + "vulnIntegrityImpact" : "LOW", + "vulnAvailabilityImpact" : "LOW", + "subConfidentialityImpact" : "NONE", + "subIntegrityImpact" : "NONE", + "subAvailabilityImpact" : "NONE", + "exploitMaturity" : "NOT_DEFINED", + "confidentialityRequirement" : "NOT_DEFINED", + "integrityRequirement" : "NOT_DEFINED", + "availabilityRequirement" : "NOT_DEFINED", + "modifiedAttackVector" : "NOT_DEFINED", + "modifiedAttackComplexity" : "NOT_DEFINED", + "modifiedAttackRequirements" : "NOT_DEFINED", + "modifiedPrivilegesRequired" : "NOT_DEFINED", + "modifiedUserInteraction" : "NOT_DEFINED", + "modifiedVulnConfidentialityImpact" : "NOT_DEFINED", + "modifiedVulnIntegrityImpact" : "NOT_DEFINED", + "modifiedVulnAvailabilityImpact" : "NOT_DEFINED", + "modifiedSubConfidentialityImpact" : "NOT_DEFINED", + "modifiedSubIntegrityImpact" : "NOT_DEFINED", + "modifiedSubAvailabilityImpact" : "NOT_DEFINED", + "Safety" : "NOT_DEFINED", + "Automatable" : "NOT_DEFINED", + "Recovery" : "NOT_DEFINED", + "valueDensity" : "NOT_DEFINED", + "vulnerabilityResponseEffort" : "NOT_DEFINED", + "providerUrgency" : "NOT_DEFINED" + } + } ], + "cvssMetricV31" : [ { + "source" : "cna@vuldb.com", + "type" : "Secondary", + "cvssData" : { + "version" : "3.1", + "vectorString" : "CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:L/I:L/A:L", + "baseScore" : 6.3, + "baseSeverity" : "MEDIUM", + "attackVector" : "NETWORK", + "attackComplexity" : "LOW", + "privilegesRequired" : "LOW", + "userInteraction" : "NONE", + "scope" : "UNCHANGED", + "confidentialityImpact" : "LOW", + "integrityImpact" : "LOW", + "availabilityImpact" : "LOW" + }, + "exploitabilityScore" : 2.8, + "impactScore" : 3.4 + }, { + "source" : "nvd@nist.gov", + "type" : "Primary", + "cvssData" : { + "version" : "3.1", + "vectorString" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:N/A:N", + "baseScore" : 7.5, + "baseSeverity" : "HIGH", + "attackVector" : "NETWORK", + "attackComplexity" : "LOW", + "privilegesRequired" : "NONE", + "userInteraction" : "NONE", + "scope" : "UNCHANGED", + "confidentialityImpact" : "HIGH", + "integrityImpact" : "NONE", + "availabilityImpact" : "NONE" + }, + "exploitabilityScore" : 3.9, + "impactScore" : 3.6 + } ], + "cvssMetricV2" : [ { + "source" : "cna@vuldb.com", + "type" : "Secondary", + "cvssData" : { + "version" : "2.0", + "vectorString" : "AV:N/AC:L/Au:S/C:P/I:P/A:P", + "baseScore" : 6.5, + "accessVector" : "NETWORK", + "accessComplexity" : "LOW", + "authentication" : "SINGLE", + "confidentialityImpact" : "PARTIAL", + "integrityImpact" : "PARTIAL", + "availabilityImpact" : "PARTIAL" + }, + "baseSeverity" : "MEDIUM", + "exploitabilityScore" : 8.0, + "impactScore" : 6.4, + "acInsufInfo" : false, + "obtainAllPrivilege" : false, + "obtainUserPrivilege" : false, + "obtainOtherPrivilege" : false, + "userInteractionRequired" : false + } ] + }, + "weaknesses" : [ { + "source" : "cna@vuldb.com", + "type" : "Secondary", + "description" : [ { + "lang" : "en", + "value" : "CWE-74" + }, { + "lang" : "en", + "value" : "CWE-89" + } ] + }, { + "source" : "nvd@nist.gov", + "type" : "Primary", + "description" : [ { + "lang" : "en", + "value" : "CWE-89" + } ] + } ], + "configurations" : [ { + "nodes" : [ { + "operator" : "OR", + "negate" : false, + "cpeMatch" : [ { + "vulnerable" : true, + "criteria" : "cpe:2.3:a:anisha:job_recruitment:1.0:*:*:*:*:*:*:*", + "matchCriteriaId" : "56E6381D-BF5F-4DC1-A525-4DEDA44D5C56" + } ] + } ] + } ], + "references" : [ { + "url" : "https://code-projects.org/", + "source" : "cna@vuldb.com", + "tags" : [ "Product" ] + }, { + "url" : "https://github.com/UnrealdDei/cve/blob/main/sql11.md", + "source" : "cna@vuldb.com", + "tags" : [ "Exploit", "Third Party Advisory" ] + }, { + "url" : "https://vuldb.com/?ctiid.289917", + "source" : "cna@vuldb.com", + "tags" : [ "Permissions Required", "VDB Entry" ] + }, { + "url" : "https://vuldb.com/?id.289917", + "source" : "cna@vuldb.com", + "tags" : [ "Third Party Advisory", "VDB Entry" ] + }, { + "url" : "https://vuldb.com/?submit.473107", + "source" : "cna@vuldb.com", + "tags" : [ "Third Party Advisory", "VDB Entry" ] + } ] + } + }, { + "cve" : { + "id" : "CVE-2025-22144", + "sourceIdentifier" : "security-advisories@github.com", + "published" : "2025-01-13T20:15:29.817", + "lastModified" : "2025-05-13T15:42:53.440", + "vulnStatus" : "Analyzed", + "cveTags" : [ ], + "descriptions" : [ { + "lang" : "en", + "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." + }, { + "lang" : "es", + "value" : "NamelessMC es un software gratuito, fácil de usar y potente para servidores de Minecraft. Un usuario con permisos admincp.core.emails o admincp.users.edit puede validar usuarios y un atacante puede restablecer sus contraseñas. Cuando la cuenta se aprueba correctamente por correo electrónico, el código de restablecimiento es NULL, pero cuando la cuenta es validada manualmente por un usuario con permisos admincp.core.emails o admincp.users.edit, el código de restablecimiento ya no será NULL, sino que estará vacío. Un atacante puede solicitar http://localhost/nameless/index.php?route=/forgot_password/&c= y restablecer la contraseña. Como resultado, un atacante puede comprometer la contraseña de otro usuario y tomar el control de su cuenta. Este problema se ha solucionado en la versión de lanzamiento 2.1.3 y se recomienda a todos los usuarios que la actualicen. No se conocen Workarounds para esta vulnerabilidad." + } ], + "metrics" : { + "cvssMetricV40" : [ { + "source" : "security-advisories@github.com", + "type" : "Secondary", + "cvssData" : { + "version" : "4.0", + "vectorString" : "CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:L/VA:N/SC:H/SI:L/SA:N/E:X/CR:X/IR:X/AR:X/MAV:X/MAC:X/MAT:X/MPR:X/MUI:X/MVC:X/MVI:X/MVA:X/MSC:X/MSI:X/MSA:X/S:X/AU:X/R:X/V:X/RE:X/U:X", + "baseScore" : 9.0, + "baseSeverity" : "CRITICAL", + "attackVector" : "NETWORK", + "attackComplexity" : "LOW", + "attackRequirements" : "PRESENT", + "privilegesRequired" : "NONE", + "userInteraction" : "NONE", + "vulnConfidentialityImpact" : "HIGH", + "vulnIntegrityImpact" : "LOW", + "vulnAvailabilityImpact" : "NONE", + "subConfidentialityImpact" : "HIGH", + "subIntegrityImpact" : "LOW", + "subAvailabilityImpact" : "NONE", + "exploitMaturity" : "NOT_DEFINED", + "confidentialityRequirement" : "NOT_DEFINED", + "integrityRequirement" : "NOT_DEFINED", + "availabilityRequirement" : "NOT_DEFINED", + "modifiedAttackVector" : "NOT_DEFINED", + "modifiedAttackComplexity" : "NOT_DEFINED", + "modifiedAttackRequirements" : "NOT_DEFINED", + "modifiedPrivilegesRequired" : "NOT_DEFINED", + "modifiedUserInteraction" : "NOT_DEFINED", + "modifiedVulnConfidentialityImpact" : "NOT_DEFINED", + "modifiedVulnIntegrityImpact" : "NOT_DEFINED", + "modifiedVulnAvailabilityImpact" : "NOT_DEFINED", + "modifiedSubConfidentialityImpact" : "NOT_DEFINED", + "modifiedSubIntegrityImpact" : "NOT_DEFINED", + "modifiedSubAvailabilityImpact" : "NOT_DEFINED", + "Safety" : "NOT_DEFINED", + "Automatable" : "NOT_DEFINED", + "Recovery" : "NOT_DEFINED", + "valueDensity" : "NOT_DEFINED", + "vulnerabilityResponseEffort" : "NOT_DEFINED", + "providerUrgency" : "NOT_DEFINED" + } + } ], + "cvssMetricV31" : [ { + "source" : "nvd@nist.gov", + "type" : "Primary", + "cvssData" : { + "version" : "3.1", + "vectorString" : "CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:H", + "baseScore" : 9.8, + "baseSeverity" : "CRITICAL", + "attackVector" : "NETWORK", + "attackComplexity" : "LOW", + "privilegesRequired" : "NONE", + "userInteraction" : "NONE", + "scope" : "UNCHANGED", + "confidentialityImpact" : "HIGH", + "integrityImpact" : "HIGH", + "availabilityImpact" : "HIGH" + }, + "exploitabilityScore" : 3.9, + "impactScore" : 5.9 + } ] + }, + "weaknesses" : [ { + "source" : "security-advisories@github.com", + "type" : "Secondary", + "description" : [ { + "lang" : "en", + "value" : "CWE-610" + } ] + }, { + "source" : "nvd@nist.gov", + "type" : "Primary", + "description" : [ { + "lang" : "en", + "value" : "CWE-640" + } ] + } ], + "configurations" : [ { + "nodes" : [ { + "operator" : "OR", + "negate" : false, + "cpeMatch" : [ { + "vulnerable" : true, + "criteria" : "cpe:2.3:a:namelessmc:nameless:*:*:*:*:*:*:*:*", + "versionEndExcluding" : "2.1.3", + "matchCriteriaId" : "4402A4C5-43C3-4436-B207-1374812080DD" + } ] + } ] + } ], + "references" : [ { + "url" : "https://github.com/NamelessMC/Nameless/releases/tag/v2.1.3", + "source" : "security-advisories@github.com", + "tags" : [ "Release Notes" ] + }, { + "url" : "https://github.com/NamelessMC/Nameless/security/advisories/GHSA-p883-7496-x35p", + "source" : "security-advisories@github.com", + "tags" : [ "Exploit", "Vendor Advisory" ] + }, { + "url" : "https://github.com/NamelessMC/Nameless/security/advisories/GHSA-p883-7496-x35p", + "source" : "134c704f-9b21-4f2e-91b3-4a467353bcc0", + "tags" : [ "Exploit", "Vendor Advisory" ] + } ] + } + }, { + "cve" : { + "id" : "CVE-2025-23124", + "sourceIdentifier" : "416baaa9-dc9f-4396-8d5f-8c081fb06d67", + "published" : "2025-01-11T15:15:08.930", + "lastModified" : "2025-01-11T15:15:08.930", + "vulnStatus" : "Rejected", + "cveTags" : [ ], + "descriptions" : [ { + "lang" : "en", + "value" : "Rejected reason: This CVE ID has been rejected or withdrawn by its CVE Numbering Authority." + } ], + "metrics" : { }, + "references" : [ ] + } + }, { + "cve" : { + "id" : "CVE-2025-23125", + "sourceIdentifier" : "416baaa9-dc9f-4396-8d5f-8c081fb06d67", + "published" : "2025-01-11T15:15:09.023", + "lastModified" : "2025-01-11T15:15:09.023", + "vulnStatus" : "Rejected", + "cveTags" : [ ], + "descriptions" : [ { + "lang" : "en", + "value" : "Rejected reason: This CVE ID has been rejected or withdrawn by its CVE Numbering Authority." + } ], + "metrics" : { }, + "references" : [ ] + } + } ] +}