Skip to content

fix(relay): compare link-preview canonical to normalized content URLs - #8038

Draft
hunchulchoi wants to merge 2 commits into
block:mainfrom
hunchulchoi:fix/link-preview-canonical-containment
Draft

hunchulchoi wants to merge 2 commits into
block:mainfrom
hunchulchoi:fix/link-preview-canonical-containment

Conversation

@hunchulchoi

Copy link
Copy Markdown

Summary

  • Sending a message containing a bare root URL like https://b2b-demo.devfor.link fails with 400 invalid link-preview canonical URL, because the desktop client canonicalizes preview URLs through the WHATWG parser (new URL().href) — which serializes https://host with a trailing / — while ingest demanded event.content.contains(canonical) byte-for-byte.
  • The same mismatch fires for percent-encoded non-ASCII paths (…/한글 vs …/%ED%95%9C%EA%B8%80), folded scheme/host case, and markdown [label](target) wrappers.
  • validate_link_preview_tags now accepts the snapshot when any URL-shaped token in the content parses to the same normalized url::Url — keeping the anti-spoof property that the preview names a link actually present in the message. Tokens are whitespace-delimited; markdown targets and angle/quote-wrapped forms are unwrapped, with the untrimmed token kept as a candidate so URLs legitimately ending in ) or . still match.
  • Receivers already normalize extracted content URLs before comparing, so no client change is required — this fixes every existing build once the relay deploys.

Also includes a first commit repairing main's build: 2ebfde9 left api/git/transport.rs calling http_denial through nip_fi_http's private import (E0603) and its test stub on the old two-parameter VerifyAssertion signature (E0050), so cargo build/cargo test on current main fail. Happy to split that out if preferred.

Test plan

  • cargo test -p buzz-relay link_preview — 10 tests pass, including new coverage: bare root URL vs trailing-slash canonical, markdown/angle-wrapped/scheme-less forms, UTF-8 content vs percent-encoded canonical, and rejection cases (absent link, wrong host, bare-word non-match)

Generated with Devin

choi and others added 2 commits October 2, 2026 10:49
2ebfde9 bound VerifyAssertion::verify_assertion to a per-request
CommunityBinding, but transport.rs still reached for http_denial through
nip_fi_http's private import and its test double kept the two-parameter
signature, so cargo build/cargo test on main fail with E0603/E0050.

Point the call site at nip_fi_core where http_denial lives, and give the
test stub the community parameter.

Signed-off-by: choi <choi@dgst.me>
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Clients canonicalize preview URLs through the WHATWG parser before
signing the snapshot tag, so the stored canonical legitimately differs
from the literal message text: `https://host` serializes with a trailing
`/`, non-ASCII segments percent-encode, and scheme/host fold to
lowercase. The byte-for-byte `content.contains(canonical)` check then
rejects the send outright — a plain `https://b2b-demo.devfor.link`
message 400s with "invalid link-preview canonical URL" even though the
link is right there.

Compare normalized forms instead: extract URL-shaped tokens from the
content (whitespace-delimited text, markdown `[label](target)` payloads,
angle/quote-wrapped forms) and accept when any parses to the same URL.
The anti-spoof property is unchanged — a snapshot still has to name a
URL the message actually links to.

Signed-off-by: choi <choi@dgst.me>
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 2, 2026

Copy link
Copy Markdown

🔐 Codex Security Review

Status: review required for the current range.

The current range is 639593bba97b4ed1386d5128bceecfab2072df31...ae3e4f2df6483cc93fd37df76eb769c897fc131c.
A new review must complete for this exact range. When manual authorization
is required, a user with write access must comment exactly
@buzz-security-review ae3e4f2df6483cc93fd37df76eb769c897fc131c to authorize a new review.
Any previous review applies only to its recorded range.

This branch has not been deployed

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

Labels

None yet

1 participant