feat(apidocs): Lint undeclared serializers on public endpoints - #123919
Merged
Conversation
This was referenced Sep 9, 2026
azulus
force-pushed
the
jeremy/input-rules-1-ratchet-and-declared
branch
from
September 9, 2026 17:17
54d6ca8 to
94f77b8
Compare
azulus
marked this pull request as ready for review
September 9, 2026 17:25
gricha
approved these changes
Sep 9, 2026
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 94f77b8. Configure here.
A public endpoint that validates its input with one serializer while naming a different one in @extend_schema accepts a shape no generated client can send. The schema is built from what is declared; the runtime accepts what validates. Twelve endpoints currently disagree, six on the query string and six on the body. S025 reports a serializer built from request.GET or request.data that is absent from parameters= or request=. The callee must look like a class, which skips plain calls that happen to take data= and classes chosen at runtime, neither of which the schema could name. Also adds ENFORCED, naming the rules whose diagnostics are fatal. It ships empty so nothing here can fail a build, mirroring how the Response[T] linter rolled out. SENTRY_INPUT_LINT_ALL=1 emits every rule regardless, so a plain flake8 run over the tree is the backlog inventory. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@extend_schema(request=...) accepts a mapping of media type to serializer, and two release-file endpoints declare their body that way. _declared_names only unwrapped a list or tuple, so the serializer in a mapping counted as undeclared and S025 would report an endpoint that had in fact declared what it validates with. Latent rather than live: neither endpoint currently validates with the serializer it declares, so nothing was misreported. Fixing it now because the rule's usefulness rests on producing no false positives. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
azulus
force-pushed
the
jeremy/input-rules-1-ratchet-and-declared
branch
from
September 9, 2026 17:56
5d8f486 to
7bb98e5
Compare
Contributor
Sentry Snapshot Testing
No base snapshot found for |
azulus
added a commit
that referenced
this pull request
Sep 9, 2026
…3921) Reports a parameter read straight off `request.GET` or `request.data` instead of through a serializer. Such a parameter is invisible twice over. The schema has nothing to generate from, so no client can send it, and the value arrives as an unchecked string, so a misspelled key fails in production rather than in review. Reading through a serializer named in `@extend_schema` fixes both at once, because drf-spectacular derives the documented parameters from the same field objects the handler validates with. Ten public endpoints already work this way; this rule reports everyone else. Three diagnostics share the rule, separating cases that differ in how they get fixed: a literal key read straight off the input, a key computed at runtime that no schema could ever document, and the whole dict handed to a callable so that what it reads is unknowable from the call site. Counting or iterating the dict is not a hand-off, building a serializer from it is the target rather than a violation, and reads through `validated_data` are accepted. A read has to hang off `request` or `self.request` to count. `serializer.data` and `response.data` are outputs a handler builds, not parameters a client sent, and subscript writes such as `request.data["title"] = default` are not reads either. The rule is absent from `ENFORCED`, so nothing gates. The backlog is 197 raw reads, two computed keys, and 17 hand-offs. Follows #123919, which added the ratchet this rule hangs off. --------- Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Adds S025, which reports a public endpoint that validates its input with one serializer while naming a different one in
@extend_schema.The schema is generated from what an endpoint declares, while the runtime accepts whatever validates, so when those disagree the endpoint accepts a shape no generated client can send. Twelve endpoints disagree today, six on the query string and six on the request body.
project_repo.pyis representative: it declares aninline_serializercarrying onlyrepositoryId, then validates the body withProjectRepoSerializer.This is the first of three rules covering endpoint input, so it also adds
ENFORCED, which names the rules whose diagnostics are fatal. It ships empty and nothing here can fail a build. That mirrors how theResponse[T]linter rolled out — land the rule dark, drain the backlog, then gate — andSENTRY_INPUT_LINT_ALL=1emits every rule regardless so a plain flake8 run over the tree produces the inventory to drain.S025 only fires when the callee looks like a class. That skips plain calls which happen to take a
data=argument, and classes chosen at runtime such asserializer_cls(data=...); the schema could not name either of those, so reporting them would be noise rather than backlog.