fix(gif): Giphy thumbnails blocked by CSP + posted GIFs render frozen (LC-507) #474

Merged
longjacksonle merged 2 commits from fix/LC-507-gif-csp-and-autoplay into main 2026-06-28 04:09:23 +02:00

Fixes two GIF defects, both fallout from the LC-505 Tenor to Giphy migration. Root cause diagnosed for each before patching.

Bug 1: picker thumbnails blocked (broken-image icons)

The Giphy search proxy works (titles + "Powered by GIPHY" render), but every grid cell showed a broken image. Root cause: the CSP img-src directive in server/src/security_headers.rs still allowed only https://tenor.com https://*.tenor.com. The picker grid hotlinks Giphy preview thumbnails from media.giphy.com, which CSP blocked. Not an API-key, rendition, or referrer issue. Fix: allow https://*.giphy.com (drop the dead Tenor entry), with a regression assertion in server/tests/security_headers.rs.

Bug 2: posted GIFs render frozen until clicked

Root cause: uploads::pipeline::process_gif intentionally builds the preview rendition as a single still first frame, and the timeline rendered image attachments with ?size=preview (server/templates/partials/attachment.html). So a posted GIF showed a frozen frame; the lightbox opened the animated original, which is why it animated on click.

Decision: autoplay inline (Slack/Discord behavior). A GIF picker exists to post animated reactions and users expect motion; the GIFs are byte-capped and re-hosted same-origin. Fix: serve the animated original for image/gif attachments in the timeline (new Attachment::is_gif()); all other images keep the still preview.

Checks

just check, just test, just test-saas all pass.

Fixes two GIF defects, both fallout from the LC-505 Tenor to Giphy migration. Root cause diagnosed for each before patching. ## Bug 1: picker thumbnails blocked (broken-image icons) The Giphy search proxy works (titles + "Powered by GIPHY" render), but every grid cell showed a broken image. Root cause: the CSP `img-src` directive in `server/src/security_headers.rs` still allowed only `https://tenor.com https://*.tenor.com`. The picker grid hotlinks Giphy preview thumbnails from `media.giphy.com`, which CSP blocked. Not an API-key, rendition, or referrer issue. Fix: allow `https://*.giphy.com` (drop the dead Tenor entry), with a regression assertion in `server/tests/security_headers.rs`. ## Bug 2: posted GIFs render frozen until clicked Root cause: `uploads::pipeline::process_gif` intentionally builds the preview rendition as a single still first frame, and the timeline rendered image attachments with `?size=preview` (`server/templates/partials/attachment.html`). So a posted GIF showed a frozen frame; the lightbox opened the animated original, which is why it animated on click. Decision: autoplay inline (Slack/Discord behavior). A GIF picker exists to post animated reactions and users expect motion; the GIFs are byte-capped and re-hosted same-origin. Fix: serve the animated original for `image/gif` attachments in the timeline (new `Attachment::is_gif()`); all other images keep the still preview. ## Checks `just check`, `just test`, `just test-saas` all pass.
The LC-505 Tenor to Giphy migration left CSP img-src pointing at the dead
Tenor CDN, so the picker grid's hotlinked media.giphy.com thumbnails were
blocked and rendered as broken images. Swap the allowance to *.giphy.com and
lock it in with a regression assertion.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
fix(gif): autoplay posted GIFs inline instead of showing a frozen frame (LC-507)
All checks were successful
check-secrets / TruffleHog (push) Successful in 4s
check-secrets / Nosey parker (push) Successful in 4s
check-secrets / Kingfisher (push) Successful in 6s
check-secrets / TruffleHog (pull_request) Successful in 5s
check-secrets / Kingfisher (pull_request) Successful in 5s
check-secrets / Nosey parker (pull_request) Successful in 5s
Check / clippy + fmt + tests (pull_request) Successful in 3m54s
Create release / Create release from merged PR (pull_request) Has been skipped
675c1163f9
Image attachments rendered the still first-frame preview rendition in the
timeline, so a posted GIF looked frozen until the lightbox opened the animated
original. Serve the animated original for image/gif attachments (Slack/Discord
behavior); other images keep the still preview.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-06-28 04:07:53 +02:00
longjacksonle deleted branch fix/LC-507-gif-csp-and-autoplay 2026-06-28 04:09:23 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/lets-chat!474
No description provided.