Skip to content

fix: reject scalar JSON bodies on v1 write endpoints - #9912

Merged
dheeru0198 merged 2 commits into
previewfrom
fix/v1-non-object-json-body
Sep 29, 2026
Merged

dheeru0198 merged 2 commits into
previewfrom
fix/v1-non-object-json-body

Conversation

@pablohashescobar

@pablohashescobar pablohashescobar commented Sep 29, 2026 •

Copy link
Copy Markdown
Member

Description

v1 write endpoints returned HTTP 500 when the JSON body was a scalar ("hello", 42, true). The parser accepts those values, then views call request.data.get() or .pop() and raise AttributeError.

BaseAPIView and BaseViewSet now reject that body in initial() for POST, PUT, and PATCH. If the parsed body is not a dict or a list, the request raises ParseError and returns 400. Object and array bodies are unchanged, and GET does not read the body.

Type of Change

  • Bug fix (non-breaking change which fixes an issue)
  • Feature (non-breaking change which adds functionality)
  • Improvement (change that would cause existing functionality to not work as expected)
  • Code refactoring
  • Performance improvements
  • Documentation update

Screenshots and Media (if applicable)

Test Scenarios

  • POST /api/v1/workspaces/{slug}/projects/{project_id}/issues/{issue_id}/comments/ with a JSON body of "hello", 42, or true and confirm the response is 400.
  • POST the same endpoint with {"comment_html": "<p>hi</p>"} and confirm the response is 201.
  • POST an endpoint that accepts a JSON array (estimate points) and confirm it still succeeds.
  • docker compose -f docker-compose-test.yml run --rm api-tests pytest plane/tests/contract/api/test_non_object_json_body.py

References

  • No work item ID.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • Write requests with scalar JSON bodies now return a clear HTTP 400 error. Object and array bodies are not rejected by the request validation.

- Views call request.data.get()/.pop(), so a scalar JSON body such as
  "hello", 42 or true raised AttributeError and returned HTTP 500.
- Add an initial() hook to BaseAPIView and BaseViewSet that raises
  ParseError for POST/PUT/PATCH when the parsed body is not a dict or
  list, so every subclass gets a 400 instead of a crash.
- Add contract tests covering scalar bodies (400) and a normal object
  body (still 201).
Copilot AI balanced review requested due to automatic review settings September 29, 2026 07:45
@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: d591f2c5-aa86-4f7d-814b-7298d407ca58

📥 Commits

Reviewing files that changed from the base of the PR and between a49ec5c and 606beb1.

📒 Files selected for processing (3)
  • apps/api/plane/api/views/base.py
  • apps/api/plane/tests/contract/api/test_non_object_json_body.py
  • apps/api/plane/tests/unit/views/test_non_object_json_body.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/api/plane/api/views/base.py

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

Base API views now validate parsed request bodies for POST, PUT, and PATCH. Scalar JSON bodies produce HTTP 400 responses. Unit and contract tests also cover object and array bodies.

Changes

Write Request Body Validation

Layer / File(s) Summary
Base view body validation
apps/api/plane/api/views/base.py
BaseAPIView and BaseViewSet raise ParseError when parsed data for a POST, PUT, or PATCH request is neither a dictionary nor a list.
Body validation tests
apps/api/plane/tests/unit/views/test_non_object_json_body.py, apps/api/plane/tests/contract/api/test_non_object_json_body.py
Unit tests cover scalar, object, and array bodies across both base view classes and write methods. Contract tests check scalar bodies on six write targets, object-body comment creation, and array-body handling.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~10 minutes

Change: Bug fix

Merge Risk: ⚪ Minimal · up to 606be

Scalar JSON bodies on POST, PUT, and PATCH are rejected, while supported form and multipart bodies pass the shared checks. No actionable merge-blocking risk is established.

Security Architecture Review

Security architecture risk: 🔵 Low · up to 606be

Scalar JSON bodies are rejected earlier on affected write endpoints. The reviewed paths show no new access to protected operations, but the checks do not establish behavior for every endpoint using these base views.

Retained concerns
No architecture-level concerns identified.

Security review details

Security Blast Radius

  • inferred — The changed gate affects attacker-supplied bodies on POST, PUT, and PATCH routes that inherit either API base class; the six tested targets are not a complete route inventory.

Trust Boundaries and Controls

  • inferred — The added shape check does not appear to bypass the base classes’ authentication or permission initialization: it executes after superclass initialization, and those control declarations remain in place. Anonymous-request behavior was not directly tested in the supplied cases.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main change: rejecting scalar JSON bodies on v1 write endpoints.
Description check ✅ Passed The description explains the defect, implementation, expected behavior, selected change type, test scenarios, and references. The optional screenshots section is not applicable, and the omitted null-b…
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

The error text excludes valid arrays, and coverage omits the duplicated viewset branch and supported method/body combinations.

Review effort: Balanced
Findings: 2 Low severity

Open (2)
What changed in this PR

Rejects scalar JSON payloads on v1 write endpoints to prevent AttributeError responses.

Changes:

  • Adds scalar-body validation to both v1 base view classes.
  • Adds contract tests for scalar rejection and object acceptance.
File Description
apps/​api/​plane/​api/​views/​base.py Validates POST, PUT, and PATCH body shapes.
apps/​api/​plane/​tests/​contract/​api/​test_non_object_json_body.py Tests scalar rejection and object acceptance.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread apps/api/plane/api/views/base.py Outdated
super().initial(request, *args, **kwargs)
# Views call request.data.get()/.pop(); a scalar JSON body would 500.
if request.method in ("POST", "PUT", "PATCH") and not isinstance(request.data, (dict, list)):
raise ParseError("Request body must be a JSON object.")
Comment thread apps/api/plane/api/views/base.py Outdated
Comment on lines +169 to +173
def initial(self, request, *args, **kwargs):
super().initial(request, *args, **kwargs)
# Views call request.data.get()/.pop(); a scalar JSON body would 500.
if request.method in ("POST", "PUT", "PATCH") and not isinstance(request.data, (dict, list)):
raise ParseError("Request body must be a JSON object.")
Copilot flagged the ParseError text, which told clients only an object was valid while the guard also accepts arrays. Cover POST, PUT, and PATCH on both BaseAPIView and BaseViewSet, including an array that is allowed through.
@dheeru0198
dheeru0198 merged commit 26e43c9 into preview Sep 29, 2026
16 checks passed
@dheeru0198
dheeru0198 deleted the fix/v1-non-object-json-body branch September 29, 2026 09:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants