mirror of
https://github.com/block/buzz.git
synced 2026-10-27 12:40:30 +00:00
fix(relay): reject unreadable write targets as missing (#8249)
🤖 A sender who cannot use or read a referenced event, artifact or workflow now gets exactly the response it would get for an ID that does not exist. #8197 (merged) set this rule for deletions; this applies it to the other writes that look up a target, plus the workflow read and webhook endpoints. Artifact `create` and workflow definition saves are the exceptions, described below. Access is checked before any message that depends on the target, readability goes through `event_visible_to_reader` (including #8197's private-deletion rule), and soft-deleted targets keep matching missing ones. | Path | Checks, in order | Response for any unusable target | |---|---|---| | Reaction (kind 7) | target lookup → `event_visible_to_reader` → every channel gate for the derived channel, including archived | `invalid: reaction target event not found` | | Edit (kind 40003) | target in the edit's `h` channel and readable → author / agent-owner check, including an author since removed from a private channel | `invalid: edit target not found or not editable by you` | | Forum vote (kind 45002) | target in the vote's `h` channel and readable → kind check | `invalid: vote target event not found` | | Report by event (kind 1984 `e`) | `event_visible_to_reader` → token channel scope → channel membership or open | `invalid: report target event not found` | | Reply (NIP-10 and workflow `send_message` replies) | parent in the reply's channel and readable | `reply parent not found` | | Artifact update / delete / restore / move | head exists and is homed in `h` (move: in the source the sender can write) → revision, type, deleted and root checks | `conflict: artifact head unavailable` | | Workflow trigger (kind 46020) | workflow exists and sender is its owner | `invalid: workflow not found or not triggerable by you` | | `GET /workflows/{id}/runs` and `/runs/{run_id}/approvals` | workflow exists, has a channel, and the caller can read it | `404 {"error":"workflow not found"}` | | `POST /hooks/{id}` | workflow exists and the provided secret matches its stored secret → definition and trigger-type checks | `404 {"error":"workflow not found"}` | Two of these reject writes that used to be accepted: a reaction to an event the sender cannot read (such as someone else's author-only event), and a report on an event the reporter cannot read. Reports by pubkey and by blob are unchanged. In an archived channel, writes whose channel comes from their target (reactions and deletions) get that target's not-found response, and an artifact move out of an archived source channel gets `conflict: artifact head unavailable` like any other source the sender cannot write; every other write keeps `invalid: channel is archived`. A webhook call with an absent or wrong secret, or to a workflow without a webhook secret, now gets `404` instead of `401`/`400`. Artifact `create` and workflow definition saves still report a taken ID, as NIP-AR allows for artifacts. Removed messages, none of which any client depends on from these endpoints: `edit target event not found`, `must be event author to edit`, `target event belongs to a different channel`, `target event has no channel`, `parent event belongs to a different channel`, `parent event has no channel association`, `artifact home changed`, `workflow is not accessible`, `workflow is not channel-scoped`, the webhook's `authentication failed` and `webhook secret required but not configured`, `forbidden: not authorized to trigger this workflow` for non-owners (it remains for an owner whose channel authority lapsed), and `restricted: not a channel member` for an edit by an author removed from a private channel. `e2e_existence_oracle` checks each event write over HTTP and WS, and the workflow read and webhook endpoints over HTTP, against missing, soft-deleted, other-channel, private-channel, archived, author-only and wrong-kind targets as applicable, with a positive control per path. It runs in the Backend Integration job alongside `e2e_author_only_deletion`. `removed_author_edit_does_not_reveal_target` checks that a removed author's edits of their remaining message, their deleted message and a missing ID get identical answers. In the PostgreSQL suite, `head_outside_authorized_channel_matches_missing_head` covers the artifact transaction, `workflow_reply_to_unusable_parent_matches_missing` covers workflow replies through `RelayActionSink::send_message`, `scoped_token_report_does_not_reveal_target` and `scoped_token_reaction_does_not_reveal_target` cover channel-scoped tokens, and `channelless_workflow_runs_read_as_missing` covers the runs read for a workflow with no channel. --------- Signed-off-by: Will Pfleger <pfleger.will@gmail.com> Co-authored-by: Hayt <211b96e6a2b7f45fd4047988976c7bbbeeda0c15f3ae7b32eec20834b5a55118@buzz.block.builderlab.xyz>
This commit is contained in:
3
.github/workflows/_ci-relay.yml
vendored
3
.github/workflows/_ci-relay.yml
vendored
@@ -111,6 +111,7 @@ jobs:
|
||||
--lib \
|
||||
--test e2e_event_reminder \
|
||||
--test e2e_author_only_deletion \
|
||||
--test e2e_existence_oracle \
|
||||
--archive-file target/ci/backend-integration-tests.tar.zst
|
||||
- name: Build PostgreSQL test archive
|
||||
if: steps.relay-artifacts-cache.outputs.cache-hit != 'true' && matrix.artifact == 'postgres-tests'
|
||||
@@ -581,7 +582,7 @@ jobs:
|
||||
run: |
|
||||
cargo nextest run \
|
||||
--archive-file target/ci/backend-integration-tests.tar.zst \
|
||||
-E 'binary(e2e_event_reminder) | binary(e2e_author_only_deletion)' \
|
||||
-E 'binary(e2e_event_reminder) | binary(e2e_author_only_deletion) | binary(e2e_existence_oracle)' \
|
||||
--run-ignored ignored-only
|
||||
env:
|
||||
RELAY_URL: ws://localhost:3000
|
||||
|
||||
Reference in New Issue
Block a user