Skip to content

fix(cli): reject empty message content in buzz messages send - #7490

Open
SomSamantray wants to merge 3 commits into
block:mainfrom
SomSamantray:fix/cli-messages-send-empty-content
Open

fix(cli): reject empty message content in buzz messages send#7490
SomSamantray wants to merge 3 commits into
block:mainfrom
SomSamantray:fix/cli-messages-send-empty-content

Conversation

@SomSamantray

Copy link
Copy Markdown
Contributor

Summary

buzz messages send --content - published a whitespace-only kind-9 message when stdin carried only whitespace. It exited 0 and reported "message": "", so a caller whose pipeline had failed got a success response and a ghost empty bubble in the channel.

The command now refuses to publish empty or whitespace-only content:

  • Before: printf '%s\n' "$UNSET_VAR" | buzz messages send --channel <UUID> --content - -> exit 0, empty message published.
  • After: the same command -> exit 1, {"error":"user_error","message":"refusing to publish an empty message from stdin (an upstream pipeline step likely failed). Pass --allow-empty to confirm.","retryable":false}, nothing published.

Key decisions:

  • The guard reads the trimmed content, not raw emptiness. The reported repro produces "\n", so an is_empty() check would have missed it.
  • It runs before the mention preflight and before any upload, so a rejected send costs no relay round-trip.
  • It applies to content from any source, including a literal --content "". That is deliberately stronger than the notes set / mem set guards, which only check their stdin branch.
  • A send that attaches a real file stays valid with empty text, because media assembly makes the published content non-empty. A blank --file value is not an attachment and does not exempt the guard.
  • --allow-empty is the escape hatch, matching notes set and mem set.
  • read_or_stdin is unchanged, so the other commands that share it are unaffected.

Related issue

Fixes #7448.

No existing PR addresses this issue (open and closed PRs searched).

Testing

  • cargo test -p buzz-cli -> 472 unit tests and 3 process-level tests pass.
  • cargo fmt --all -- --check and cargo clippy -p buzz-cli --all-targets -- -D warnings are clean.
  • crates/buzz-cli/tests/empty_content_guard.rs runs the real binary with piped stdin and asserts the exit code and stderr contract for empty, whitespace-only, and --allow-empty sends. Flipping the guard's sentinel derivation, or removing the guard, fails it.
  • Manual, against an unreachable relay: empty and whitespace-only stdin exit 1 with the stdin diagnosis; --content "" exits 1 with the inline diagnosis; --content "" --allow-empty clears the guard and fails at the network layer; --content "" --file /nonexistent still reports upload failed; --content "" --mention <hex64> reports the empty-content error rather than the membership error.

Follow-up work deferred to a future PR

  • messages edit --content "" and messages send-diff --diff - still publish empty content.
  • Content made only of zero-width characters (U+200B, U+FEFF) passes trim() and still publishes.
  • messages send buffers stdin unbounded before the size check, unlike the capped reads in notes set and mem set.

`buzz messages send --content -` published a whitespace-only kind-9 event
when stdin carried only whitespace, exited 0, and reported success. A broken
upstream pipeline therefore produced a ghost message with no signal that
anything failed.

Reject empty or whitespace-only content with a Usage error before the
mention preflight and before any upload, and add `--allow-empty` as an
explicit opt-out mirroring `notes set` and `mem set`. A send that attaches
a file stays valid because media assembly makes the published content
non-empty.

The guard reads the trimmed content, so the reported repro
(`printf '%s\n' "$UNSET_VAR"`) is caught; a raw `is_empty()` check would
miss it.

Signed-off-by: Som Samantray <som.samantray@gmail.com>
Review follow-up for the `messages send` empty-content guard.

- Extract `content_comes_from_stdin` so the `--content -` sentinel has one
  production site, and cover it with a falsifiable predicate test.
- Add a process-level test that runs the real binary with piped stdin and
  asserts the exit code and stderr contract. Flipping the dispatch sentinel
  to a constant, or removing the guard, now fails it.
- Treat a blank `--file` value as no attachment, so it cannot exempt the
  guard and mask the empty-content diagnosis behind an upload failure.
- Assert the inline and stdin error wordings separately, and drop two
  `query_count` assertions that could not observe the round-trip they
  claimed to rule out.
- Cover whitespace-only content with `--allow-empty`.

Signed-off-by: Som Samantray <som.samantray@gmail.com>
Signed-off-by: Som Samantray <som.samantray@gmail.com>
@SomSamantray
SomSamantray requested a review from a team as a code owner September 8, 2026 18:39
@github-actions

github-actions Bot commented Sep 8, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is 44316ff72f5f7de014c66b01cbf534298a70c249...a6c604786be087de9a10d0c3c64fe6a04b6af942.
A new review must complete for this exact range. When manual authorization
is required, a Block organization member must comment exactly
@buzz-security-review a6c604786be087de9a10d0c3c64fe6a04b6af942 to authorize a new review.
Any previous review applies only to its recorded range.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(cli): buzz messages send --content - publishes an empty message when stdin is empty; should be a hard error

1 participant