Fix handling of SVGs (#31359)

* Fix handling of SVGs

1. Ensure we always include thumbnails for them
2. Show `m.file` handler if we cannot render the SVG
3. When opening ImageView use svg thumbnail if the SVG cannot be rendered

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix UploadConfirmDialog choking under React devmode

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Fix test

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

* Add tests

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>

---------

Signed-off-by: Michael Telatynski <7t3chguy@gmail.com>
This commit is contained in:
Michael Telatynski
2025-12-01 16:00:09 +00:00
committed by GitHub
parent afa186cdf4
commit 45ab536737
10 changed files with 389 additions and 22 deletions

View File

@@ -64,8 +64,7 @@ export async function decryptFile(file?: EncryptedFile, info?: MediaEventInfo):
// they introduce XSS attacks if the Blob URI is viewed directly in the
// browser (e.g. by copying the URI into a new tab or window.)
// See warning at top of file.
let mimetype = info?.mimetype ? info.mimetype.split(";")[0].trim() : "";
mimetype = getBlobSafeMimeType(mimetype);
const mimetype = getBlobSafeMimeType(info?.mimetype?.split(";")[0].trim() ?? "");
return new Blob([dataArray], { type: mimetype });
} catch (e) {

View File

@@ -14,6 +14,7 @@ import { LazyValue } from "./LazyValue";
import { type Media, mediaFromContent } from "../customisations/Media";
import { decryptFile } from "./DecryptFile";
import { type IDestroyable } from "./IDestroyable";
import { getBlobSafeMimeType } from "./blobs.ts";
// TODO: We should consider caching the blobs. https://github.com/vector-im/element-web/issues/17192
@@ -82,7 +83,7 @@ export class MediaEventHelper implements IDestroyable {
.downloadSource()
.then((r) => r.blob())
// Set the mime type from the event info on the blob
.then((blob) => blob.slice(0, blob.size, content.info?.mimetype ?? blob.type))
.then((blob) => blob.slice(0, blob.size, getBlobSafeMimeType(content.info?.mimetype ?? blob.type)))
);
};
@@ -107,7 +108,9 @@ export class MediaEventHelper implements IDestroyable {
fetch(thumbnailHttp)
.then((r) => r.blob())
// Set the mime type from the event info on the blob
.then((blob) => blob.slice(0, blob.size, content.info?.thumbnail_info?.mimetype ?? blob.type))
.then((blob) =>
blob.slice(0, blob.size, getBlobSafeMimeType(content.info?.thumbnail_info?.mimetype ?? blob.type)),
)
);
};

View File

@@ -66,8 +66,20 @@ const ALLOWED_BLOB_MIMETYPES = [
"audio/x-flac",
];
/**
* Checks whether the given mime type is in the allowed mimetype list
* @param mimetype - the mimetype to check
*/
export function isMimeTypeAllowed(mimetype: string): boolean {
return ALLOWED_BLOB_MIMETYPES.includes(mimetype);
}
/**
* Returns the input mimetype if it is allowed, `application/octet-stream` otherwise
* @param mimetype - the mimetype to check
*/
export function getBlobSafeMimeType(mimetype: string): string {
if (!ALLOWED_BLOB_MIMETYPES.includes(mimetype)) {
if (!isMimeTypeAllowed(mimetype)) {
return "application/octet-stream";
}
return mimetype;