Skip to content

fix: verify relay-sourced events before the app acts on them - #663

Open
AndreaDiazCorreia wants to merge 5 commits into
fix/transport-downgrade-protectionfrom
fix/relay-sourced-event-authentication
Open

fix: verify relay-sourced events before the app acts on them#663
AndreaDiazCorreia wants to merge 5 commits into
fix/transport-downgrade-protectionfrom
fix/relay-sourced-event-authentication

Conversation

@AndreaDiazCorreia

Copy link
Copy Markdown
Member

Depends on #659

Cut from fix/transport-downgrade-protection, which is where the public NostrUtils.isValidEventSignature this builds on landed. Merge #659 first, or review against that branch.

What

Four intakes accepted events straight off a relay without establishing that they were what they claimed to be. Every subscription pins kinds and authors in its filter, but the pinned dart_nostr fork parses relay EVENT frames into a NostrEvent without verifying the signature or matching the frame against the filter that was sent — so both state what was asked for, and the author on the way back is the relay's claim.

  • Order book (38383). Admitted anything carrying z=order: no kind check, no author check, no signature. This feeds the amounts, premiums and maker rating shown before a user decides to trade.
  • Relay list (10002). Parsing checked only the event kind. Verifying in fromEvent also makes publishedAt meaningful, since the freshness comparison downstream is against created_at. Cleartext ws:// is refused on the same terms as the manual-entry path, and one event can no longer contribute an unbounded number of relays.
  • NWC (13194 / 23195 / 23196). None of the three receive paths verified anything. The response listener also failed the whole request on any decrypt or parse error, so an unusable event ended it before the wallet's real response could arrive; unusable events are now ignored and only the timeout ends the wait.
  • Deep link relay fetch. Link-supplied relays were merged into settings.relays, with a restore that could not work: it read the settings getter after it had already been replaced. Restoring correctly would not have helped either — dart_nostr's init is additive and the fork has no per-relay disconnect. The fetch now runs on a scoped Nostr instance, closed when it ends.

Notes

  • Behavior change: a fetch with specificRelays reaches only those relays, not the union with the app's. The deep link path already falls back to the app's own relays when no candidate is found there.
  • NostrEvent.type drops its bang assert: an event without a z tag reached it before any other check and threw inside the stream callback, where onError does not catch it. One caller in lib/.
  • One existing test asserted that throwing, and was inverted.
  • The relay-list test helper builds signed events instead of assembling one with a placeholder id and sig.
  • flutter analyze clean; flutter test 1142 passing.

@coderabbitai

coderabbitai Bot commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

🗂️ Base branches to auto review (1)
  • develop

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: cdf87a1f-b3e5-4ef6-9106-15010c8af87b

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a1748bf79e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +63 to +70
if (relays.length > maxRelays) {
logger.w(
'Relay list from ${event.pubkey} names ${relays.length} relays; '
'keeping the first $maxRelays',
);
relays = relays.sublist(0, maxRelays);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Apply the relay cap after filtering and deduplication

When a signed relay list contains more than 50 raw r tags, this truncates the raw list before validRelays removes malformed, insecure, or duplicate entries. For example, 50 duplicate or invalid tags followed by the node's usable relays produces an empty or one-relay active set and discards all later valid relays, potentially leaving the app unable to reach the Mostro instance. Normalize, validate, and deduplicate first, then cap the relays that can actually be contributed.

Useful? React with 👍 / 👎.

@AndreaDiazCorreia
AndreaDiazCorreia force-pushed the fix/transport-downgrade-protection branch from 14f83f2 to 444af66 Compare August 24, 2026 23:55
@AndreaDiazCorreia
AndreaDiazCorreia force-pushed the fix/relay-sourced-event-authentication branch 2 times, most recently from 2915ef1 to 455778b Compare August 25, 2026 00:33

@ermeme ermeme Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes requested

The signature checks at the order-book, relay-list, and NWC boundaries are valuable, but two relay-sourced paths still undermine the stated trust boundary:

  1. Relay cap before validation/deduplication (lib/core/models/relay_list_event.dart:55-103): the 50-entry cap is applied to raw r tags. A signed list beginning with 50 duplicate, malformed, or insecure URLs can crowd out all later usable relays, leaving no viable connection. Normalize, validate, and deduplicate first, then cap the usable relay set.

  2. Unsigned deep-link event intake (lib/services/nostr_service.dart:350-405 and 410-496): both fetchEventById and fetchOrderInfoByEventId accept a relay-provided ID/kind/pubkey without NostrUtils.isValidEventSignature(event). A malicious relay selected by a deep link can fabricate an unsigned event claiming the configured Mostro pubkey and have it parsed/presented as an order. Verify the selected event's signature before parsing in both paths, with forged and retagged-event coverage.

git diff --check is clean. GitHub reports no current-head CI check run, and Flutter is unavailable in this review environment.

@ermeme ermeme Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Supplemental blocking finding

Stale signed order events can overwrite newer open-order state (lib/data/repositories/open_orders_repository.dart:100-134). The new intake verifies kind, author, signature, and tags, but unconditionally assigns _events[orderId] = event. A relay can replay an older genuinely signed kind-38383 event for the same d tag after a newer one arrived, replacing the displayed amount, premium, rating, or status with stale state. The PR already applies deterministic NIP-01 replacement ordering to kind-38385 info events; apply the same created_at then lower-event-id tie-break to order events and add newer-then-older regression coverage.

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