Skip to content

fix: reject non-object JSON bodies on /predict endpoint (#819)#832

Open
Rudra-clrscr wants to merge 5 commits into
Userunknown84:mainfrom
Rudra-clrscr:feature/819-predict-input-validation
Open

fix: reject non-object JSON bodies on /predict endpoint (#819)#832
Rudra-clrscr wants to merge 5 commits into
Userunknown84:mainfrom
Rudra-clrscr:feature/819-predict-input-validation

Conversation

@Rudra-clrscr

Copy link
Copy Markdown
Contributor

Summary

Closes #819.

Most of the requested validation on /predict (missing text, empty/whitespace
text, non-string type, max length, consistent {"error": ...} format) was
already implemented. The remaining gap: data = request.get_json(silent=True) or {}
assumed the parsed JSON body was always a dict. A valid-but-non-object body
(e.g. [1,2,3], "hello", 42) made data.get("text") raise AttributeError,
which the catch-all handler turned into a 500 that leaked the raw exception
string. Malformed JSON also produced a misleading "No text provided" message
instead of flagging the bad body.

Changes

  • Explicitly check the parsed JSON body type in /predict
  • Non-dict JSON bodies (list/string/number) now return 400 with a clear message
  • Malformed/non-JSON bodies now return 400 instead of falling through to
    "No text provided"

Test plan

  • backend/tests/test_predict_input_validation.py::test_non_object_body_rejected
    was already written for this and failing before the fix; passes now
  • Ran the full backend test suite (pytest backend/tests/) — no regressions
    from this change (102 tests pass; 6 pre-existing failures unrelated to this
    issue, caused by an undefined internal_endpoint_required name and other
    unrelated bugs already present on main)

…4#819)

The /predict route already validated missing/empty text, wrong types,
and max length, but request.get_json(silent=True) or {} assumed a
dict — a valid JSON body that wasn't an object (list, string, number)
caused data.get("text") to raise AttributeError, which the catch-all
handler turned into a 500 leaking the exception message. Malformed
JSON bodies fell through to a misleading "No text provided" message.

Now both cases return a clear 400 with a consistent {"error": ...}
payload, matching the existing test_predict_input_validation.py
expectations.
Copilot AI review requested due to automatic review settings July 13, 2026 17:31
@vercel

vercel Bot commented Jul 13, 2026

Copy link
Copy Markdown

Someone is attempting to deploy a commit to the Aditya Sharma's projects Team on Vercel.

A member of the Team first needs to authorize it.

Copilot AI 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.

Pull request overview

This PR hardens the Flask ML API’s /predict endpoint input validation to prevent 500s when the request body is valid JSON but not an object, and to return clearer 400-level errors for invalid JSON bodies.

Changes:

  • Adds explicit type-checking for the parsed JSON body in /predict to require a JSON object (dict).
  • Returns 400 for malformed/non-JSON bodies instead of allowing the request to fall through to later validation and/or exceptions.
  • Returns 400 for non-dict JSON bodies (e.g., list/string/number) with an explanatory error message.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread backend/api.py
Comment thread backend/api.py
Rudra-clrscr added 2 commits July 14, 2026 04:38
The container's Flask process defaults to FLASK_HOST=127.0.0.1 (a
deliberate secure default for bare-metal runs), so inside the CI
container it never listens on an interface reachable through the
-p 5000:5000 port mapping, and the smoke test's curl always times out.
docker-compose.yml already sets FLASK_HOST=0.0.0.0 for the same reason;
the CI workflow's docker run step needs the same override.
main's copies of these files have a duplicate 'function App()'
declaration and a stray '#Manipulation' line before an import
respectively - both invalid JS that break 'vite build' in the frontend
Docker image stage. Only fixed inside the open fix/frontend-eslint-errors
PR branch, never merged to main, so any branch cut from main (including
this one) inherits them. Pulling in the already-verified fix.
}
};

// Helper to get earned badges (returns array of badge objects)

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This code removed .

} finally {
setLoading(false);

function App() {

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

This code also removed

const [hasCelebrated, setHasCelebrated] = useState(() => {
return localStorage.getItem("firstPrediction") === "true";
});
const [showCelebration, setShowCelebration] = useState(false);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

53to 58 also removed

@Userunknown84

Copy link
Copy Markdown
Owner

@Rudra-clrscr some code is removed from app.jsx as the other pr also link if i merge one then code base reflect fix this after i merge all.

Rudra-clrscr added 2 commits July 14, 2026 18:57
App.jsx and ManipulationIndex.jsx changes pulled in a Docker-CI-only
fix from an unrelated PR and made this PR's diff show large,
unrelated deletions (dead badge/celebration state cleanup that
belongs to the frontend-eslint-errors PR, not this one). Reverting so
review stays focused on the actual /predict input-validation fix.
The Docker frontend-build check on this PR will fail again until the
frontend-eslint-errors (or automated-test-suite) PR merges first -
expected, since main itself still carries the parse bugs those PRs fix.
… payloads

get_json(silent=True) returns None both for malformed JSON and for the
valid JSON literal 'null', so the previous code told them apart only
by whether the raw body was non-empty and then reported both cases as
'must be a valid JSON object' - misleading for a well-formed 'null'
body. Now a genuinely malformed body is distinguished by attempting a
real json.loads() parse, and valid non-object bodies are covered by
new parametrized tests (list/string/number/null).
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add input validation on the /predict endpoint

3 participants