public static async Task VerifyAvatarOrThrow(HttpClient client, string url, bool isFullSizeImage = false) { if (url.Length > Limits.MaxUriLength) { throw Errors.UrlTooLong(url); } // List of MIME types we consider acceptable var acceptableMimeTypes = new[] { "image/jpeg", "image/gif", "image/png" // TODO: add image/webp once ImageSharp supports this }; if (!PluralKit.Core.MiscUtils.TryMatchUri(url, out var uri)) { throw Errors.InvalidUrl(url); } url = TryRewriteCdnUrl(url); var response = await client.GetAsync(url); if (!response.IsSuccessStatusCode) // Check status code { throw Errors.AvatarServerError(response.StatusCode); } if (response.Content.Headers.ContentLength == null) // Check presence of content length { throw Errors.AvatarNotAnImage(null); } if (!acceptableMimeTypes.Contains(response.Content.Headers.ContentType.MediaType)) // Check MIME type { throw Errors.AvatarNotAnImage(response.Content.Headers.ContentType.MediaType); } if (isFullSizeImage) { // no need to do size checking on banners return; } if (response.Content.Headers.ContentLength > Limits.AvatarFileSizeLimit) // Check content length { throw Errors.AvatarFileSizeLimit(response.Content.Headers.ContentLength.Value); } // Parse the image header in a worker var stream = await response.Content.ReadAsStreamAsync(); var image = await Task.Run(() => Image.Identify(stream)); if (image == null) { throw Errors.AvatarInvalid; } if (image.Width > Limits.AvatarDimensionLimit || image.Height > Limits.AvatarDimensionLimit) // Check image size { throw Errors.AvatarDimensionsTooLarge(image.Width, image.Height); } }