Skip to content

fix(#28): reject unknown fields in POST and PATCH /items endpoints - #29

Open
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/28-reject-unknown-fields
Open

fix(#28): reject unknown fields in POST and PATCH /items endpoints#29
fullsend-ai-coder[bot] wants to merge 1 commit into
mainfrom
agent/28-reject-unknown-fields

Conversation

@fullsend-ai-coder

Copy link
Copy Markdown

Summary

  • Add allowed-field validation to POST /items and PATCH /items/:id endpoints so requests containing unknown JSON fields return HTTP 400 with a descriptive error instead of silently dropping the fields and returning 200
  • POST /items accepts only name; PATCH /items/:id accepts only name and done
  • Add 5 new tests covering: unknown-only fields on PATCH, mixed known/unknown on PATCH, valid-only on PATCH, unknown fields on POST, and valid-only on POST

Root cause

Both create_item() and update_item() in app.py only checked for known fields (name, done) and ignored any additional keys in the request body. This caused silent data loss — callers believed updates persisted when they did not.

Testing

All 13 tests pass (pytest -v). Pre-existing ruff lint errors (import sorting, timezone.utc alias) are on unchanged lines and are not addressed by this change.


Closes #28

Post-script verification

  • Branch is not main/master (agent/28-reject-unknown-fields)
  • Secret scan passed (gitleaks — 88e54cc8611dff32b36a33934937eb8ab394eeb7..HEAD)
  • PR body secret scan passed (gitleaks — no-git)

Both create_item() and update_item() silently dropped unknown JSON
fields while returning HTTP 200, causing callers to believe updates
persisted when they did not. This is a data-loss risk for clients
sending fields like warehouse_bin that the API does not recognize.

Add allowed-field validation to both endpoints: POST /items accepts
only {"name"}, PATCH /items/:id accepts only {"name", "done"}.
Requests with unknown fields now return HTTP 400 with an error
message listing the unrecognized field names.

Add tests covering unknown-only fields, mixed known/unknown fields,
and valid-only fields for both endpoints.

Note: pre-existing ruff lint errors (I001 import sorting, UP017
timezone.utc alias) exist in unchanged lines and are not addressed
by this change.

Closes #28
@fullsend-ai-review

fullsend-ai-review Bot commented Aug 21, 2026

Copy link
Copy Markdown

🤖 Finished Review · ✅ Success · Started 7:10 PM UTC · Completed 7:33 PM UTC

Commit: 7f31ff4 · View workflow run →

@fullsend-ai-review

Copy link
Copy Markdown

Review

Findings

Medium

  • [nil/null handling] app.py:52 — In update_item, request.get_json() can return None (empty body, wrong content type). The new validation at line 52 calls set(data.keys()) without a null guard, causing an unhandled AttributeError and HTTP 500. Unlike create_item, which guards with if not data or "name" not in data:, update_item has no equivalent check before the field validation block.
    Remediation: Add if not data: return jsonify(error="request body is required"), 400 after data = request.get_json() and before the allowed set definition.

  • [scope-creep] app.py:29 — POST /items validation logic was added but is not authorized by issue Bug: PATCH /items/:id returns 200 but drops unknown fields without error #28. The issue exclusively describes the PATCH /items/:id bug ("PATCH /items/:id returns 200 but drops unknown fields without error"). While POST exhibits the same silent-drop behavior, the fix extends beyond the linked issue's scope.
    Remediation: Update issue Bug: PATCH /items/:id returns 200 but drops unknown fields without error #28 to include POST /items, or split the POST changes into a separate issue and PR.

Low

  • [incomplete-api-contract] README.md:11 — The endpoint table documents POST /items and PATCH /items/:id with example payloads but does not mention that unknown fields are now rejected with HTTP 400.
    Remediation: Update descriptions to note that only specific fields are accepted.

  • [test-inadequate] test_app.py — No test covers sending a PATCH request with an empty or missing JSON body, which would expose the null-handling crash in update_item.
    Remediation: Add a test that sends a PATCH with no JSON body and asserts a 400 response.

  • [error-message-format] app.py:32 — Error messages at lines 32 and 54 begin with uppercase "Unknown fields" but the established codebase pattern uses lowercase (e.g., "name is required", "item not found").
    Remediation: Change both to lowercase: error=f"unknown fields: {', '.join(sorted(unknown))}".


Labels: PR modifies API endpoint request validation logic and fixes a reported bug


Next steps:

  • /fs-fix — agent addresses review findings automatically
  • /fs-fix <your instruction> — agent fixes with your specific guidance
  • Push commits directly — review re-runs automatically on push
  • /fs-fix-stop — disable automatic fix runs for this PR

@fullsend-ai-review fullsend-ai-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

See the review comment for full details.

Comment thread app.py

data = request.get_json()
allowed = {"name", "done"}
unknown = set(data.keys()) - allowed

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] nil/null handling

In update_item, request.get_json() can return None (empty body, wrong content type). The new validation at line 52 calls set(data.keys()) without a null guard, causing an unhandled AttributeError and HTTP 500. Unlike create_item, which guards with 'if not data or "name" not in data:' before the field validation block, update_item has no equivalent check.

Suggested fix: Add 'if not data: return jsonify(error="request body is required"), 400' after 'data = request.get_json()' and before the 'allowed' set definition.

Comment thread app.py
if not data or "name" not in data:
return jsonify(error="name is required"), 400

allowed = {"name"}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[medium] scope-creep

POST /items validation logic was added but is not authorized by issue #28. The issue exclusively describes the PATCH /items/:id bug. While POST exhibits the same silent-drop behavior, the fix extends beyond the linked issue's scope.

Suggested fix: Update issue #28 to include POST /items, or split the POST changes into a separate issue and PR.

Comment thread app.py
allowed = {"name"}
unknown = set(data.keys()) - allowed
if unknown:
return jsonify(error=f"Unknown fields: {', '.join(sorted(unknown))}"), 400

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[low] error-message-format

Error messages at lines 32 and 54 begin with uppercase 'Unknown fields' but the established codebase pattern uses lowercase (e.g., 'name is required', 'item not found').

Suggested fix: Change both to lowercase: error=f"unknown fields: {', '.join(sorted(unknown))}"

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

Labels

area/api API and HTTP endpoint issues bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: PATCH /items/:id returns 200 but drops unknown fields without error

1 participant