Skip to content

database corruption with missing sanitization in Express 5 #2036

Description

@ElectricNroff

CVE Services v2.8.5 depends on "express": "^4.22.2" whereas CVE Services v.2.8.6 depends on "express": "^5.2.1" instead.

These differ in the behavior of:

query(['state']).optional().isString().trim().customSanitizer(val => { return val.toUpperCase() }).isIn(MODIFYTARGETS).withMessage(errorMsgs.ID_MODIFY_STATES),

In 2.8.5, a rejected value of state is converted to REJECTED before reaching the controller, and thus PUT /cve-id/:id?state=rejected calls end up with "state": "REJECTED" in the CveId document.

In 2.8.6, PUT /cve-id/:id?state=rejected calls end up with "state": "rejected" in the CveId document. This is database corruption (achievable by a CNA with no extra privileges) in the sense that the lowercase version is not one of the allowed values:

CVE_STATES: {
PUBLISHED: 'PUBLISHED',
RESERVED: 'RESERVED',
REJECTED: 'REJECTED',
AVAILABLE: 'AVAILABLE'
},

For example, client applications may not be able to display that state, or make correct calculations with that state data. Also, three API calls to count the number of RESERVED, PUBLISHED, and REJECTED CVE IDs for a CNA would not sum to the total number of CVE IDs for that CNA.

Example: CVE-2026-21803 on cveawg-test.mitre.org (the test.cve.org website is not able to display this)

Also, in 2.8.5, PUT /cve-id/:id?state=re%C5%BFerved calls fail with a 400 HTTP status code and an error message about invalid CVE ID state.

In 2.8.6, PUT /cve-id/:id?state=re%C5%BFerved calls have a 200 HTTP status code end up with "state": "re\u017ferved" in the CveId document, such as CVE-2026-21805 on cveawg-test.mitre.org.

The source code has a mention of different req.query behavior in Express 5, but not all places in the application account for this:

// Express 5 exposes req.query as a getter which reparses the URL on every
// access. express-validator sanitizers therefore cannot persist changes on
// req.query. Copy the validator context instead, while retaining the flat
// dotted keys expected by the legacy controllers.
function reqCtxValidatedQueryMapping (req, keys) {

Also, there are other affects on CVE Services users.

In 2.8.5, a POST call such as https://cveawg-test.mitre.org/api/cve-id?short_name=exampleCNA&batch_type=NONSEQUENTIAL&cve_year=2026&amount=5 would have obtained 5 CVE IDs.

In 2.8.6, that same POST call fails with:

HTTP/2 400

{"error":"INVALID_BATCH_TYPE","message":"The batch_type provided is invalid. Available values are sequential, nonsequential, non-sequential."}

because the lowercasing in:

query(['batch_type']).optional().isString().trim().customSanitizer(val => { return val.toLowerCase() }),

is not actually effective. Thus, for example, if a CNA has their own application to reserve CVE IDs, and happens to use uppercase, and stores the API key in a secrets manager that isn't accessible by their whole CNA staff, they could be unable to reserve CVE IDs until they find a person who is able to deploy an update.

Also, the Express 5 configuration in CVE Services disallows consecutive '/' characters in a URL in a number of cases where Express 4 allows that. This was mentioned by a CNA on Slack, discussing POST /cve-id. However, there are also thousands of non-CNA users making GET /cve/:id calls, and presumably a fraction of them have code to access URLs such as https://cveawg.mitre.org/api//cve/CVE-1999-0001 - i.e., with extra '/' characters. Those would have suddenly stopped working.

In other words, there is an unknown scope of loss of backward compatibility, and some of the compatibility differences are related to input validation that is currently not functional with CVE Services on Express 5.

Part of the motivation for Express 5 may have been:

https://github.com/CVEProject/cve-services/commit/859a0cb36ee144304fad846f603f0272c6eba267

Bumps [qs](https://github.com/ljharb/qs) to 6.16.0 and updates ancestor dependency [express](https://github.com/expressjs/express). These dependencies need to be updated together.

It's no longer true that Express 5 is needed for qs 6.16.0. In https://github.com/expressjs/express/releases/tag/v4.22.3 there was a change to allow qs 6.16.0 with Express 4 - with the normal dependency mechanisms. There was, admittedly, a point in time where a vulnerability in qs 6.15.x had been announced, and the only available qs update was 6.16.0, which was not immediately known to be compatible with Express 4: expressjs/express#7440

This time has passed, and it's once again viable to use the latest qs with the latest Express 4.

It's possible that the best solution is to revert to Express 4.22.3 now, restoring compatibility, and then reconsider 5.x after more of the application code has been changed or validated against 5.x.

No activity

Activity on this issue will appear here.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions