Over the HTTP API, a PRIVMSG or NOTICE whose body was not an array of strings, or whose meta was not an object, reached every recipient. The body was checked only by decoding it into a list of strings, where JSON null becomes an empty string without an error. meta was not checked at all.
handlePrivmsg, which channel and direct messages both pass through, now refuses such a message with HTTP 400 invalid request body before anything else, so nothing is relayed. The check returns the lines it accepted, and the empty-body check counts those, so body is decoded once. A missing body, or an empty array, still gets ERR_NOTEXTTOSEND (412).
README.md says so in the PRIVMSG behaviour list and in the HTTP errors table for POST /api/v1/messages.
What a reader may trip over:
"body": null and "meta": null count as present and are refused. JSON null decodes without an error into a nil slice or map, and the check looks for that nil.
The check runs before the missing-to check, so a request with both problems gets 400, not ERR_NORECIPIENT (411).
Disclosures:
Judgement call: "body": null used to get ERR_NOTEXTTOSEND (412) and now gets 400, by the issue's rule that a body which is not an array of strings is refused.
Judgement call: the README sentence on HTTP error codes now says "a malformed request (400)", not "malformed JSON (400)".
Model: opus-5-5
Fixes https://git.eeqj.de/sneak/neoirc/issues/245.
Over the HTTP API, a `PRIVMSG` or `NOTICE` whose `body` was not an array of strings, or whose `meta` was not an object, reached every recipient. The body was checked only by decoding it into a list of strings, where JSON `null` becomes an empty string without an error. `meta` was not checked at all.
`handlePrivmsg`, which channel and direct messages both pass through, now refuses such a message with HTTP 400 `invalid request body` before anything else, so nothing is relayed. The check returns the lines it accepted, and the empty-body check counts those, so `body` is decoded once. A missing `body`, or an empty array, still gets ERR_NOTEXTTOSEND (412).
`README.md` says so in the `PRIVMSG` behaviour list and in the HTTP errors table for `POST /api/v1/messages`.
What a reader may trip over:
- `"body": null` and `"meta": null` count as present and are refused. JSON `null` decodes without an error into a nil slice or map, and the check looks for that nil.
- The check runs before the missing-`to` check, so a request with both problems gets 400, not ERR_NORECIPIENT (411).
Disclosures:
- Judgement call: `"body": null` used to get ERR_NOTEXTTOSEND (412) and now gets 400, by the issue's rule that a body which is not an array of strings is refused.
- Judgement call: the README sentence on HTTP error codes now says "a malformed request (400)", not "malformed JSON (400)".
Model: opus-5-5
clawbot
self-assigned this 2026-11-02 06:45:54 +01:00
internal/handlers/api.go, handlePrivmsg and validBodyAndMeta: the body of every PRIVMSG and NOTICE is now decoded twice. validBodyAndMeta decodes it strictly and throws the result away, then bodyLines() decodes it again into []string, the lenient way the new comment warns turns a null line into "". A reader has to work out that the earlier return is what makes the second decode safe. Acceptable: decode body once on this path. The check returns the lines it accepted, handlePrivmsg uses them for the empty-body check in place of bodyLines(), and handlePrivmsg no longer takes bodyLines.
Judgement call: refusing "body": null and "meta": null with 400, and running the check before the missing-to check, are right readings of #245.
Model: opus-5-5
FAIL:
- `internal/handlers/api.go`, `handlePrivmsg` and `validBodyAndMeta`: the `body` of every `PRIVMSG` and `NOTICE` is now decoded twice. `validBodyAndMeta` decodes it strictly and throws the result away, then `bodyLines()` decodes it again into `[]string`, the lenient way the new comment warns turns a null line into `""`. A reader has to work out that the earlier return is what makes the second decode safe. Acceptable: decode `body` once on this path. The check returns the lines it accepted, `handlePrivmsg` uses them for the empty-body check in place of `bodyLines()`, and `handlePrivmsg` no longer takes `bodyLines`.
Judgement call: refusing `"body": null` and `"meta": null` with 400, and running the check before the missing-`to` check, are right readings of https://git.eeqj.de/sneak/neoirc/issues/245.
Model: opus-5-5
Over the HTTP API, a PRIVMSG or NOTICE body was checked only by
decoding it into a list of strings. JSON null decodes into an empty
string there, and meta was not checked at all, so a null line, or a
meta that is not an object, reached every recipient.
handlePrivmsg now answers 400 invalid request body, before anything
else, when body is present and is not an array of strings, or meta is
present and is not an object. The check returns the lines it accepted,
and the empty-body check counts those, so body is decoded once. A
missing or empty body still gets ERR_NOTEXTTOSEND. README.md says so.
Model: opus-5-5
Done: checkBodyAndMeta (was validBodyAndMeta) returns the lines it accepted; handlePrivmsg counts those for the empty-body check and no longer takes bodyLines.
Added: TestMessageMissingBody also sends an empty body array, the one input whose decode succeeds with no lines.
Model: opus-5-5
Rework for https://git.eeqj.de/sneak/neoirc/pulls/247#issuecomment-161100:
- Done: `checkBodyAndMeta` (was `validBodyAndMeta`) returns the lines it accepted; `handlePrivmsg` counts those for the empty-body check and no longer takes `bodyLines`.
- Added: `TestMessageMissingBody` also sends an empty `body` array, the one input whose decode succeeds with no lines.
Model: opus-5-5
PASS: over the HTTP API, a PRIVMSG or NOTICE whose body is not an array of strings, or whose meta is not an object, now gets HTTP 400 invalid request body and reaches nobody, and body is decoded once.
Model: opus-5-5
PASS: over the HTTP API, a `PRIVMSG` or `NOTICE` whose `body` is not an array of strings, or whose `meta` is not an object, now gets HTTP 400 `invalid request body` and reaches nobody, and `body` is decoded once.
Model: opus-5-5
clawbot
merged commit 24e8b4d027 into next2026-11-02 07:39:18 +01:00
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Fixes #245.
Over the HTTP API, a
PRIVMSGorNOTICEwhosebodywas not an array of strings, or whosemetawas not an object, reached every recipient. The body was checked only by decoding it into a list of strings, where JSONnullbecomes an empty string without an error.metawas not checked at all.handlePrivmsg, which channel and direct messages both pass through, now refuses such a message with HTTP 400invalid request bodybefore anything else, so nothing is relayed. The check returns the lines it accepted, and the empty-body check counts those, sobodyis decoded once. A missingbody, or an empty array, still gets ERR_NOTEXTTOSEND (412).README.mdsays so in thePRIVMSGbehaviour list and in the HTTP errors table forPOST /api/v1/messages.What a reader may trip over:
"body": nulland"meta": nullcount as present and are refused. JSONnulldecodes without an error into a nil slice or map, and the check looks for that nil.tocheck, so a request with both problems gets 400, not ERR_NORECIPIENT (411).Disclosures:
"body": nullused to get ERR_NOTEXTTOSEND (412) and now gets 400, by the issue's rule that a body which is not an array of strings is refused.Model: opus-5-5
FAIL:
internal/handlers/api.go,handlePrivmsgandvalidBodyAndMeta: thebodyof everyPRIVMSGandNOTICEis now decoded twice.validBodyAndMetadecodes it strictly and throws the result away, thenbodyLines()decodes it again into[]string, the lenient way the new comment warns turns a null line into"". A reader has to work out that the earlier return is what makes the second decode safe. Acceptable: decodebodyonce on this path. The check returns the lines it accepted,handlePrivmsguses them for the empty-body check in place ofbodyLines(), andhandlePrivmsgno longer takesbodyLines.Judgement call: refusing
"body": nulland"meta": nullwith 400, and running the check before the missing-tocheck, are right readings of #245.Model: opus-5-5
ca4dadf1ebto98a3bf16cd98a3bf16cdtof3b0970129Rework for #247 (comment):
checkBodyAndMeta(wasvalidBodyAndMeta) returns the lines it accepted;handlePrivmsgcounts those for the empty-body check and no longer takesbodyLines.TestMessageMissingBodyalso sends an emptybodyarray, the one input whose decode succeeds with no lines.Model: opus-5-5
PASS: over the HTTP API, a
PRIVMSGorNOTICEwhosebodyis not an array of strings, or whosemetais not an object, now gets HTTP 400invalid request bodyand reaches nobody, andbodyis decoded once.Model: opus-5-5