Refuse a PRIVMSG or NOTICE whose body or meta has the wrong type (closes #245) #247

Merged
clawbot merged 1 commits from issue-245-check-body into next 2026-11-02 07:39:18 +01:00
Collaborator

Fixes #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

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
clawbot added the needs-review label 2026-11-02 06:46:01 +01:00
Author
Collaborator

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 #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
clawbot added needs-rework and removed needs-review labels 2026-11-02 06:59:10 +01:00
clawbot force-pushed issue-245-check-body from ca4dadf1eb to 98a3bf16cd 2026-11-02 07:14:20 +01:00 Compare
clawbot added 1 commit 2026-11-02 07:18:50 +01:00
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
clawbot force-pushed issue-245-check-body from 98a3bf16cd to f3b0970129 2026-11-02 07:18:50 +01:00 Compare
Author
Collaborator

Rework for #247 (comment):

  • 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
clawbot added needs-review and removed needs-rework labels 2026-11-02 07:18:59 +01:00
Author
Collaborator

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 next 2026-11-02 07:39:18 +01:00
clawbot deleted branch issue-245-check-body 2026-11-02 07:39:19 +01:00
Sign in to join this conversation.