PRC103 — Malformed request made the server fail
| Severity | medium |
| OWASP API Security Top 10 | API8:2023 Security Misconfiguration |
| Found by | procyon test (runtime) |
What it catches
For every operation that is not public and whose request body has an
example in the spec, procyon test sends, as test account A, the example
itself and then a few bodies the schema forbids, built from it in fixed
ways (never randomly), fields in name order:
- the first required field with known types (else the first field with
known types) set to a value of none of them:
"title": 42; - the first required field the example has, left out;
- the first string field with an
enum, set to a value outside it; - the first string field with a
maxLength, one character too long; or, when no string field has one, the first string field at 100 000 characters.
If a mutation gets a 5xx, or no answer within the timeout (a regular
expression that backtracks on a long string takes the server down for
everyone), it is a finding. If the unchanged example already gets a 5xx,
nothing is reported for the operation: a note says so, as it does when the
example gets any other non-2xx (a 404 would refuse every mutation for
another reason). Malformed requests run after B's checks on every write and
before the deletes, so an accepted one never hides what B can do to A's
object. After a request that got no answer, nothing more is sent: the
operations and deletes left are listed under Notes, since
the server may stay busy for minutes.
Why it matters
A 5xx means the input reached code that did not expect it: an unchecked
type assertion, a database constraint, a parser. Those paths leak stack
traces and internal details, and the ones that crash are where denial of
service and injection start.
How to fix it
Validate the request body against the schema before using it, and answer
400 with what is wrong. Most frameworks can validate against the OpenAPI
schema directly.
How to ignore it
If the finding is fine on purpose, write an x-procyon-ignore on the operation,
with the reason. The finding is still reported, under Ignored, and no
longer fails the run. See ignoring a finding.
/imports:
post:
x-procyon-ignore:
- rule: PRC103
reason: "Legacy importer, replaced in Q3; errors are not exposed"
Known limits
- A field whose schema gives no type, or composes it (
allOf,oneOf,anyOf,not), never gets a wrong type: any value may be valid. - Bodies described only through
allOf/oneOfat the top level, with noproperties, get no mutations. - Public operations (a login) are not sent malformed bodies, and neither are operations without a JSON object example.
Examples
Reported: the example is {"title": "Shopping list"}, and
POST /notes {"title": 42} (as A)
500 {"error":"internal error"}
Not reported: the same request answered 400.