## Summary
- Supersedes #1913 with a KLIPY-hosted URL implementation.
- Adds KLIPY GIF search and trending results to desktop message and
forum composers.
- Keeps selected GIFs hosted by KLIPY; Buzz stores only the external URL
and media metadata (no imeta tag, since relays only accept hash-backed
local `/media/` entries).
- Aligns the Emoji/GIF picker with Buzz's standard segmented control,
theme surfaces, and motion behavior.
## Relay-to-provider boundary
- The relay proxies KLIPY search/share so the `BUZZ_KLIPY_API_KEY` never
reaches the desktop; the key stays server-side behind a redacted `Debug`
impl.
- The dedicated GIF `reqwest` client sets `redirect::Policy::none()`.
Because the API key rides in the request path, following a provider
`3xx` could replay a key-bearing URL to an attacker-chosen host (an
SSRF/key-disclosure primitive). With redirects disabled, a `3xx` returns
as a non-success status that the handlers map to a generic `502`; the
`Location` target is never read or forwarded.
- Admission reuses the established NIP-98, membership, replay, and
per-pubkey rate-limit gates, with an upstream response-size cap and
allowlisting so KLIPY error bodies never cross the relay boundary.
## Accessibility
- Under `prefers-reduced-motion: reduce`, the picker grid renders a
static provider poster (a normalized `jpg` asset) instead of the
animated preview, or a named static placeholder when no poster is
available. It reacts to preference changes while mounted.
`no-preference` keeps the animated preview.
- Selected GIFs carry their title through `ImetaMedia`'s `displayLabel`,
so the composer thumbnail, preview dialog, editor, lightbox, and remove
control all derive one non-empty accessible name instead of an empty
`Attachment ` label. Ordinary hashed uploads keep their existing
hash-derived names.
---------
Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Kenny Lopez <klopez4212@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Signed-off-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
Co-authored-by: Princess Donut <b238ea756dee4d98afa5883fc7f1de61eeabe65bf700e3a5a5a80db5e42e2c2b@buzz.block.builderlab.xyz>
Co-authored-by: Duncan <dcfd242e557282d7a1e2cf2e6877522682f1e5c6156dc92ca7d90eaedd3b0f95@buzz.block.builderlab.xyz>
Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Co-authored-by: Hayt <9e1c23a3fd83f61da34420e4e88ff1b16e45cafcc0cd9019eb07d4ecfa8ca9b0@buzz.block.builderlab.xyz>
This change requires a valid signed Blossom authorization request and
current relay membership for every media GET and HEAD request. It
removes the unauthenticated compatibility path and updates desktop reads
to send the required authorization.
This blocks anonymous retrieval and access after relay-membership
revocation. It does not yet bind a blob to its originating channel, so
someone removed from a private channel can still read a known blob while
remaining a relay member. That channel-ACL follow-up remains required
before closing the full finding.
## Testing
- `git diff --check origin/main...codex/security-media-read-auth`
- Rebased onto `origin/main` at `5c98932`
- Full CI pending
Originating Buzz thread:
`buzz://message?channel=3928fe05-df61-4b5d-b9c7-d623b9b10ea1&id=3c6c02312f763fbe0d2bfc33a6c1a362f91d0354f3d18b039cf7a0558c1439d1`
---------
Signed-off-by: Jordan Mecom <jm@squareup.com>
Signed-off-by: Alex Rosenzweig <arosenzweig@squareup.com>
Signed-off-by: Eli Foster <efoster@squareup.com>
Co-authored-by: Eli Foster <efoster@squareup.com>
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>