feat(apidocs): Lint request parameters read without a serializer - #123921
Merged
Conversation
azulus
force-pushed
the
jeremy/input-rules-1-ratchet-and-declared
branch
from
September 9, 2026 17:17
54d6ca8 to
94f77b8
Compare
azulus
force-pushed
the
jeremy/input-rules-2-shaped-reads
branch
from
September 9, 2026 17:18
4e181df to
bc0f02d
Compare
azulus
marked this pull request as ready for review
September 9, 2026 17:26
gricha
approved these changes
Sep 9, 2026
azulus
force-pushed
the
jeremy/input-rules-1-ratchet-and-declared
branch
from
September 9, 2026 17:56
5d8f486 to
7bb98e5
Compare
azulus
force-pushed
the
jeremy/input-rules-2-shaped-reads
branch
from
September 9, 2026 17:56
bc0f02d to
8da245c
Compare
Contributor
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 8da245c. Configure here.
Base automatically changed from
jeremy/input-rules-1-ratchet-and-declared
to
master
September 9, 2026 18:49
A parameter read straight off request.GET or request.data is invisible twice. The schema has nothing to generate from, so no client can send it, and the value is an unchecked string, so a misspelled key fails at runtime 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 fields the handler validates with. Adds three diagnostics under one rule: S026 a literal key read straight off the query string or request body S027 a key computed at runtime, which no schema can document S028 the whole dict handed to a callable, so what is read is unknowable Counting or iterating the dict is not a hand-off, and building a serializer from it is the target rather than a violation, so neither is reported. Reads through validated_data are accepted. The rule is absent from ENFORCED, so nothing gates. The backlog is 208 raw reads across 67 files, 2 computed keys and 21 hand-offs. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Both review bots caught the same false positive. `_is` matched any attribute named `data`, so `serializer.data["title"]` and `response.data["title"]` were reported as raw request-body reads. Those are outputs a handler builds, not parameters a client sent, and reporting them inflates the backlog and would fail CI once the rule is enforced. The attribute now has to hang off `request` or `self.request`. `visit_Subscript` also recorded every subscript regardless of context, so `request.data["title"] = default` counted as a read of a parameter that no client supplies. It now records loads only. Together these drop the reported backlog from 208 raw reads to 197 and from 21 hand-offs to 12. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
azulus
force-pushed
the
jeremy/input-rules-2-shaped-reads
branch
from
September 9, 2026 18:52
8da245c to
4dc8b45
Compare
The guard that keeps a serializer construction from counting as a hand-off matched any call with a data= keyword, so `my_func(data=request.GET)` was silently dropped while the same call written positionally reported S028. The keyword name is not what makes a call safe; the callee being a serializer is. Both paths now share one predicate for what looks like a class, so they cannot disagree again. S025 already used that test to skip plain calls and runtime-chosen classes, and the hand-off guard skipped nothing at all. Surfaces 5 hand-offs that were dropped by both rules at once: three `serializer_cls(data=...)` sites where the class is chosen at runtime, and the integration issue-config calls that take the whole body. Those are exactly the unanalyzable cases S028 exists to report. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A read accessor on something that is not the request fell through to hand-off
detection, so `options.get("key", request.GET)` reported that the query string
was handed to `get`. The dict is a default value there, not something a callee
reads parameters out of, and naming `get` as the callee made the diagnostic
read as nonsense.
A read method now returns once handled: on the request it is the read itself,
and on anything else its arguments are ordinary lookup arguments.
No occurrences on the current tree, so the counts are unchanged.
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.

Reports a parameter read straight off
request.GETorrequest.datainstead 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_schemafixes 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_dataare accepted.A read has to hang off
requestorself.requestto count.serializer.dataandresponse.dataare outputs a handler builds, not parameters a client sent, and subscript writes such asrequest.data["title"] = defaultare 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.