Add regression tests for sanitization advisories

test(api): add regression tests for sanitization advisories (#27142)
Add regression tests for sanitization advisoriesf0f80f-test-add-regression-tests-for-sanitization-advisories-cve-2026-27886testadd regression tests sanitization advisories api

Committed by derrickmehaffy

commit message
test(api): add regression tests for sanitization advisories (#27142)

* test(api): add regression tests for sanitization advisories
 
Strapi has published five advisories that are the same vulnerability
class found through five different query surfaces, and the suite has no
test for any of them.
 
  GHSA-jjqf-j4w7-92w8  4.8.0    CVE-2023-22894  filters on private fields
  GHSA-9xg4-3qfm-9w8f  4.10.8                   same, via a `t1.` prefix
  GHSA-v8gg-4mq2-88q4  4.11.7                   content-manager relations
  GHSA-495j-h493-42q2  5.5.2                    the `lookup` param
  GHSA-rjg2-95x7-8qmx  5.37.0   CVE-2026-27886  the `where` param (9.3)
 
The leak in all of them was never in the response body — the private
field was not returned. It was in which rows came back, so asserting a
field is absent from the payload does not test the vulnerability.
 
These tests assert instead that a query referencing a private field is
indistinguishable from one that does not: either it is rejected, or it
returns exactly what the same query without that filter returns. Two
probe values are chosen so a working filter would discriminate between
them ($2 matches every bcrypt hash, $9 none), and both the status and the
result set must match. That holds whichever defence Strapi picks, so the
tests survive changes to error handling.
 
Verified to have teeth rather than passing vacuously: with removePrivate
and throwPrivate stubbed out, 8 of the 16 fail. The 8 that still pass are
covered by independent defences that were left in place — the relational
target check from CVE-2026-27886, removePassword, and param allowlisting.
 
One expectation was corrected during writing. `private: true` controls
output only; sanitizeInput applies removeRestrictedFields over
getNonWritableAttributes, which means `writable: false` plus id and
timestamps, not `private`. Writing a private field through the content
API is intended, and GHSA-gc7p-j5xm-xxh2 was fixed with an explicit
allowlist on `register` instead. The test now pins the guarantee that
actually exists: a private field a client managed to write is still never
returned, and still cannot be used as an oracle.
 
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
 
* test(api): fold advisory coverage into the files that own the behaviour
 
Addresses the review on #27142.
 
Removes tests/api/core/strapi/validation/advisory-regressions.test.api.js. Most of
what it asserted already exists elsewhere, usually with a stricter 400 assert than
the oracle-style check this file used, so it was duplicate coverage in a place
nobody would look for it.
 
Adds the three cases that were genuinely missing, into validate-query.test.api.js
next to their neighbours, each citing its advisory:
 
- filters[updatedBy][t1.password] must be rejected (GHSA-9xg4-3qfm-9w8f), added to
  the updatedBy private-fields block that already asserts 400 per field
- lookup[updatedBy][password] must be ignored (GHSA-495j-h493-42q2)
- where[updatedBy][password] must be ignored (GHSA-rjg2-95x7-8qmx)
 
The last two go in the unrecognized-top-level-params block, which already compares
a probe response against a baseline for shape equality, because silent ignoring is
the defence there rather than rejection.
 
Each case is added to both the authenticated and unauthenticated variants, matching
how that file covers every other field.
 
* test(api): assert lookup is rejected rather than ignored
 
Running the suite showed the review's assumption for GHSA-495j-h493-42q2 does not
hold: `lookup` is not silently ignored, it is rejected with a 400. The document
service throws `Invalid params: 'lookup'` for any request carrying it, because the
param is internal to draft/publish and i18n scoping.
 
Moved that case out of the shape-equality block into its own 400 assertion, and
added a `lookup` case with a public field as well, since the rejection is
unconditional and not specific to private fields.
 
`where` stays in the shape-equality block: it really is silently ignored, which
the suite confirms.
 
---------
 
Co-authored-by: Claude Opus 5 (1M context) <[email protected]>

Editor assessment

This commit patches CVE-2026-27886. The subject is accurate, test is honest for a regression suite, and the api scope is right. The body does far more than list the five advisories: it explains that the leak was never in the response body but in which rows returned, so field-absence assertions would not test the vulnerability, and it describes the oracle-style comparison used instead. It records verification that eight of sixteen cases fail when defenses are stubbed, and documents a corrected assumption about private versus writable fields. Follow-up sections fold coverage into the files that own the behavior and correct the lookup expectation after running the suite. Its test design and review history are communicated exemplarily.

Browse categories

More test examples · Back to search