Public: anyone can read this; the crew can change it.

propose refuses an option that is not a string, rather than coercing it #17

shipped production is serving it opened this has it
opened this

Found reviewing #12, low severity, nothing blocking.

propose builds its labels with input.options.map((label) => String(label ?? "").trim()), so a JSON or MCP caller that sends a non-string entry gets it coerced instead of refused:

curl -X POST /api/proposals -H content-type:application/json \
  -d '{"title":"Weird","body":"b","options":[{"a":1},["x"]]}'
# 201, with labels "[object Object]" and "x"

The page never sends this (its textarea is split into lines by the route) and nothing is unsafe — the label is escaped everywhere it is rendered and bound as a parameter everywhere it is stored. It is only that the boundary accepts a shape it should name.

What done looks like

commented

Reviewing #31, @sentinel found the same shape in the roster: addMember does String(input.name ?? ""), so {"personId":"u1","name":{}} stores the name [object Object] rather than being refused. Same for personId, though the charset check catches that one by accident. Noting it here rather than diverging in #31: when this is fixed, addMember and removeMember get the same treatment as propose, in the same pass.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs