Skip to content

fix(avatar): sniff avatar image bytes for the cached MIME type#773

Merged
mremond merged 1 commit into
mainfrom
claude/silly-chaum-d56d2f
Jun 30, 2026
Merged

fix(avatar): sniff avatar image bytes for the cached MIME type#773
mremond merged 1 commit into
mainfrom
claude/silly-chaum-d56d2f

Conversation

@mremond

@mremond mremond commented Jun 30, 2026

Copy link
Copy Markdown
Member

PEP (XEP-0084) avatar fetches in Profile.ts hardcoded image/png when caching, so animated GIF/WebP/APNG avatars were stored as a Blob typed image/png. Browsers content-sniff <img> so they still rendered, but any consumer that trusts blob.type was misled.

This derives the real type from the image's magic bytes rather than the hardcoded/advertised one — the authoritative source, and robust against clients that mislabel their avatars.

Changes

  • New packages/fluux-sdk/src/utils/imageType.tssniffImageMimeType(base64) detects PNG, APNG (animated PNG, via the acTL chunk before IDAT), GIF, WebP and JPEG from magic bytes; returns null for unrecognized data so callers fall back to image/png.
  • Wired into the three PEP cacheAvatar sites in Profile.ts: fetchAvatarData (contacts), fetchOccupantAvatar (MUC occupants), and fetchOwnAvatar (own avatar — the sniffed type also overrides a mislabeled <info type>).

Effect on consumers of blob.type

  • Avatar.tsx extracts a static first frame only when blob.type === 'image/gif'. Correctly typing GIF avatars activates that freeze-frame path (previously they were typed png and kept animating); decode failures fall back to animating, so there is no hard regression.
  • notificationAvatar.ts derives a notification file extension from blob.type; correct types yield correct extensions instead of defaulting everything to .png.

vCard-temp paths still trust the advertised <TYPE> (defaulting to png when absent) — out of scope here, but the same util could harden them.

PEP avatar fetches in Profile.ts hardcoded image/png when caching, so
animated GIF/WebP/APNG avatars were stored as a Blob typed image/png.
Browsers content-sniff <img> so they still rendered, but consumers that
trust blob.type (the GIF freeze-frame extractor, notification avatar file
extensions) were misled.

Add sniffImageMimeType() which reads the magic bytes (PNG, APNG via the
acTL chunk, GIF, WebP, JPEG) and use it at the three PEP cacheAvatar
sites: fetchAvatarData, fetchOccupantAvatar, and fetchOwnAvatar (where it
also overrides a mislabeled <info type>). Unknown formats fall back to
image/png.
@mremond mremond added this to the 0.17.0 milestone Jun 30, 2026
@mremond mremond merged commit 42bb81a into main Jun 30, 2026
3 checks passed
@mremond mremond deleted the claude/silly-chaum-d56d2f branch June 30, 2026 09:08
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