Skip to content

Reject NUL bytes before event persistence - #4700

Open
lenardhuebner88-rgb wants to merge 1 commit into
block:mainfrom
lenardhuebner88-rgb:nul-byte-validation
Open

Reject NUL bytes before event persistence#4700
lenardhuebner88-rgb wants to merge 1 commit into
block:mainfrom
lenardhuebner88-rgb:nul-byte-validation

Conversation

@lenardhuebner88-rgb

Copy link
Copy Markdown

Problem

An event whose content — or any tag value — contains a NUL byte (0x00) passes the full ingest validation and only fails inside Postgres:

Internal error: error: database error: ... invalid byte sequence for encoding "UTF8": 0x00
route:/events, status:500, accepted:false

The relay maps that database error onto a bare HTTP 500, so a plain client-side input problem is reported as an internal server error with no usable body. Reproduced with a 38-byte message — content length is irrelevant. It surfaced in practice when a client pasted text carrying a stray NUL: four sends failed in a row with relay error 500: internal server error and nothing actionable on the client side.

Change

  • crates/buzz-relay/src/handlers/ingest.rsingest_event_inner rejects NUL in event.content and in every tag value, right after the existing content-size check and before any database access, as IngestError::Rejected. The /events bridge already maps Rejected to 400 Bad Request (crates/buzz-relay/src/api/bridge.rs), so no mapper change was needed.
  • crates/buzz-cli/src/commands/messages.rscmd_send_message rejects the same input locally, before signing, and names the byte offset. No silent stripping: the content is the user's, so it is refused rather than altered.

Only 0x00 is rejected. Other control characters are valid UTF-8 and Postgres accepts them, so widening the rule would reject content that works today.

Tests

  • event_text_validation_rejects_nul_in_content
  • event_text_validation_rejects_nul_in_tag_value
  • event_text_validation_accepts_same_event_without_nul (positive control)
  • send_message_rejects_nul_locally_with_byte_offset

cargo test -p buzz-cli → 318 passed, 1 ignored, exit 0. cargo test -p buzz-relay event_text_validation → 3 passed, exit 0. No existing test was modified.

An event whose `content` or any tag value contains a NUL byte (0x00)
passes the full ingest validation and only fails inside Postgres:

    invalid byte sequence for encoding "UTF8": 0x00

The relay maps that database error onto a bare HTTP 500, so a plain
client-side input problem is reported as an internal server error with
no usable body. Reproduced with a 38-byte message; content length is
irrelevant.

Reject NUL in `event.content` and in every tag value in
`ingest_event_inner`, before any database access, as
`IngestError::Rejected` — which the `/events` bridge already maps to
400 Bad Request. The CLI rejects the same input locally before signing
and names the byte offset, so users get an actionable message instead
of a relay round-trip.

Signed-off-by: Codex Sol <codex@buzz.local>
@lenardhuebner88-rgb
lenardhuebner88-rgb requested a review from a team as a code owner August 4, 2026 10:49
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.

1 participant