diff --git a/src/api/gramjs/methods/media.ts b/src/api/gramjs/methods/media.ts index 243a68280..8eabaadb5 100644 --- a/src/api/gramjs/methods/media.ts +++ b/src/api/gramjs/methods/media.ts @@ -11,10 +11,11 @@ import { MEDIA_CACHE_MAX_BYTES, MEDIA_CACHE_NAME, MEDIA_CACHE_NAME_AVATARS, + TRANSPARENT_PIXEL, } from '../../../config'; import localDb from '../localDb'; import { getEntityTypeById } from '../gramjsBuilders'; -import { blobToDataUri } from '../../../util/files'; +import { blobToDataUri, dataUriToBlob } from '../../../util/files'; import * as cacheApi from '../../../util/cacheApi'; type EntityType = ( @@ -233,6 +234,11 @@ async function parseMedia( function prepareMedia(mediaData: ApiParsedMedia): ApiPreparedMedia { if (mediaData instanceof Blob) { + // Prevent HTML-in-video attacks + if (mediaData.type.includes('text/html')) { + return URL.createObjectURL(dataUriToBlob(TRANSPARENT_PIXEL)); + } + return URL.createObjectURL(mediaData); } diff --git a/src/config.ts b/src/config.ts index 5d5ee7b42..d1cb40095 100644 --- a/src/config.ts +++ b/src/config.ts @@ -32,6 +32,7 @@ export const MEDIA_CACHE_MAX_BYTES = 512 * 1024; // 512 KB export const CUSTOM_BG_CACHE_NAME = 'tt-custom-bg'; export const LANG_CACHE_NAME = 'tt-lang-packs-v5'; export const ASSET_CACHE_NAME = 'tt-assets'; +export const TRANSPARENT_PIXEL = 'data:image/gif;base64,R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw=='; export const DOWNLOAD_WORKERS = 16; export const UPLOAD_WORKERS = 16; diff --git a/src/util/mediaLoader.ts b/src/util/mediaLoader.ts index 071731ff4..851b3b35d 100644 --- a/src/util/mediaLoader.ts +++ b/src/util/mediaLoader.ts @@ -7,11 +7,11 @@ import { } from '../api/types'; import { - DEBUG, MEDIA_CACHE_DISABLED, MEDIA_CACHE_NAME, MEDIA_CACHE_NAME_AVATARS, + DEBUG, MEDIA_CACHE_DISABLED, MEDIA_CACHE_NAME, MEDIA_CACHE_NAME_AVATARS, TRANSPARENT_PIXEL, } from '../config'; import { callApi, cancelApiProgress } from '../api/gramjs'; import * as cacheApi from './cacheApi'; -import { fetchBlob } from './files'; +import { dataUriToBlob, fetchBlob } from './files'; import { IS_OPUS_SUPPORTED, IS_PROGRESSIVE_SUPPORTED, isWebpSupported } from './environment'; import { oggToWav } from './oggToWav'; import { webpToPng } from './webpToPng'; @@ -167,6 +167,11 @@ async function fetchFromCacheOrRemote(url: string, mediaFormat: ApiMediaFormat, function prepareMedia(mediaData: ApiParsedMedia): ApiPreparedMedia { if (mediaData instanceof Blob) { + // Prevent HTML-in-video attacks + if (mediaData.type.includes('text/html')) { + return URL.createObjectURL(dataUriToBlob(TRANSPARENT_PIXEL)); + } + return URL.createObjectURL(mediaData); }