Skip to content

chore(cats): CATS comma-joins array query params despite explode: true, causing 10 spurious findings #658

Description

@ericfitz

Summary

CATS serializes array-valued query parameters as a single comma-joined value regardless of the
parameter's declared explode: true, so the server receives one malformed value instead of repeated
keys. TMI correctly rejects it, and CATS reports the correct rejection as a failure.

Split out of #657 (the upstream example-vs-schema defect) because it is a distinct bug with its
own blast radius, and because it now costs real triage time: it produced 10 true positives in run
20260731T200650Z, three of them from HappyPath, which is exactly the signature of a genuine
server regression.

Evidence

GET /threat_models/{id}/threats (HappyPath, test 95506) — CATS sent:

?severity=low%2Clow&priority=UKADVXSDT%2CUKADVXSDT&threat_type=KSDLBKSRNSCAZ%2CKSDLBKSRNSCAZ

The server saw a single severity value of low,low, which is not in the enum, and answered:

{"error": "invalid_id", "error_description": "error in openapi3filter.RequestError: parameter \"severity\" in query has an error: Error at \"/0\": value is not one of the allowed values [\"unknown\",\"informational\",\"low\",\"medium\",\"high\",\"critical\"]"}

DELETE /threat_models/{id}/threats/bulk (HappyPath, test 97146) — same shape:

?threat_ids=2725d0d1-1e7d-460c-825d-84aff58ff8d6%2C2725d0d1-1e7d-460c-825d-84aff58ff8d6
{"error": "invalid_input", "error_description": "Invalid bulk delete request format"}

Note CATS does build the array correctly in its own JSON view of the request
("severity":["low","low"]) — it is only the wire serialization that collapses it. Per RFC 6570 /
OpenAPI style: form, explode: true, the correct encoding is severity=low&severity=low.

Impact

Affected findings

path method count
/threat_models/{threat_model_id}/threats GET 7
/threat_models/{threat_model_id}/threats/bulk DELETE 3

Options

  1. Report upstream alongside Endava/cats#206, which
    noted this as possibly sharing a code path. Preferred — it is a clear spec-conformance bug.
  2. Add a false-positive rule matching the comma-joined-array signature. Cheap, but it would also
    mask a genuine 400 on these endpoints, and it does nothing about the lost coverage.
  3. Do nothing and re-triage the same 10 findings every campaign.

Recommend 1, with 2 only as a stopgap if the campaign noise becomes a problem before upstream moves.

Acceptance Criteria

  • Confirm the behavior against the current CATS release (the run used the pinned plugin version)
  • File upstream with the two reproductions above
  • Decide whether to add an interim false-positive rule
  • Once fixed, verify the array filters actually get exercised — the coverage was never there

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    apiAPI design and implementation

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions