Back to Feed
article 3m read

Shared JSON reader started waving empty requests through

A dozen copy-pasted body parsers were replaced with one reader. Broken JSON now gets one clean 400, but the order of its checks turned away body-free deletes and let empty bodies pass the array guards.

Article DepthPractice

Flow

Audit table: which answers flipped

Compare the old and new answer for each awkward input.

  1. 1Broken JSON, right header

    Before: it depended on the route. After: always a 400 with a clear message.

  2. 2Empty body, right header

    Before: refused by routes with a list check. After: an empty list that gets past those checks.

  3. 3No body, no header

    Before: worked on the chore delete, which needs no body. After: refused.

  4. 4A single value or null

    Still reaches the route, which has to check the shape itself.

Every JSON endpoint in my scheduling app read its body its own way. I had the same two lines pasted everywhere, and each copy had drifted. Most checked for a list and sent back a 400 if they didn't get one. Login and the password-reset steps checked nothing. The chore delete treated an empty body as an empty list. And the decoder gave back the same null for broken JSON and for a real null, so nobody could tell them apart.

One reader, three checks

I swapped all the copies for one shared reader. It checks things in this order:

if (!isJsonType) return reject(400);
if (!raw) return [];
return parseOrReject(raw);

If the JSON is broken, the reader sends the 400 itself and stops the request. Broken JSON now gets the same answer on every route, and the docs say so. No complaints there.

The header check comes first

Since the header is checked first, a request with no body and no header gets refused. The chore delete can take its id from the web address with no body at all. That used to work. Now it needs a JSON header just to send nothing. I haven't checked if any client sends it that way, so I can't say if the stores saw a problem. And the new docs sentence doesn't say body-free requests need the header.

Empty now passes

With the right header, an empty body now comes back as an empty list. That gets past the list checks that used to block it. I'd guess most routes still refuse it later, because a needed field is missing. The staff save and the schedule save worry me. I suspect they don't check anything else and just say ok, but I haven't confirmed it. An ok where there used to be a 400 can hide a broken client.

A few smaller gaps: a failed read counts as empty, a body of only spaces fails while an empty one passes, and there's no size limit.

One test, one case

The test starts a small throwaway server, sends broken JSON with the right header, and expects a 400. It needs a real server because the reader ends the request on its own. Nothing tests the wrong header, the missing header, or the empty body, and those are the cases the order decides.

I tested the parser and skipped the order, and the order is where the surprises were.

Keep reading

Pick up a connected idea or branch into a nearby one.

5 paths forward