Commit 282db794 authored by Paul Meyer's avatar Paul Meyer Committed by Commit Bot

Sniff real MIME types of images to enforce unoptimized image policies.

This patch adds a function to sniff the real MIME types of images in
image_decoder. This function will be used by the unoptimized images
feature policies in order to determine the which policy to enforce for
each image.

Bug: 943203
Change-Id: I40b296a32bf49e0aa4a2e5ee9656a73eec5984ec
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1551483Reviewed-by: default avatarLeon Scroggins <scroggo@chromium.org>
Reviewed-by: default avatarChris Blume <cblume@chromium.org>
Reviewed-by: default avatarPhilip Rogers <pdr@chromium.org>
Commit-Queue: Paul Meyer <paulmeyer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#648326}
parent 881ecfec
...@@ -499,15 +499,18 @@ ImageResourceContent::UpdateImageResult ImageResourceContent::UpdateImage( ...@@ -499,15 +499,18 @@ ImageResourceContent::UpdateImageResult ImageResourceContent::UpdateImage(
// unoptimized image feature policy on |context|. // unoptimized image feature policy on |context|.
bool ImageResourceContent::IsAcceptableCompressionRatio( bool ImageResourceContent::IsAcceptableCompressionRatio(
const SecurityContext& context) { const SecurityContext& context) {
if (!image_)
return true;
uint64_t pixels = IntrinsicSize(kDoNotRespectImageOrientation).Area(); uint64_t pixels = IntrinsicSize(kDoNotRespectImageOrientation).Area();
if (!pixels) if (!pixels)
return true; return true;
DCHECK(image_);
double resource_length = double resource_length =
static_cast<double>(GetResponse().ExpectedContentLength()); static_cast<double>(GetResponse().ExpectedContentLength());
if (resource_length <= 0 && GetImage() && GetImage()->Data()) { if (resource_length <= 0 && image_->Data()) {
// WPT and LayoutTests server returns -1 or 0 for the content length. // WPT and LayoutTests server returns -1 or 0 for the content length.
resource_length = static_cast<double>(GetImage()->Data()->size()); resource_length = static_cast<double>(image_->Data()->size());
} }
// Calculate the image's compression ratio (in bytes per pixel) with both 1k // Calculate the image's compression ratio (in bytes per pixel) with both 1k
...@@ -516,12 +519,19 @@ bool ImageResourceContent::IsAcceptableCompressionRatio( ...@@ -516,12 +519,19 @@ bool ImageResourceContent::IsAcceptableCompressionRatio(
double compression_ratio_1k = (resource_length - 1024) / pixels; double compression_ratio_1k = (resource_length - 1024) / pixels;
double compression_ratio_10k = (resource_length - 10240) / pixels; double compression_ratio_10k = (resource_length - 10240) / pixels;
// Note that this approach may not always correctly identify the image (for // Attempt to sniff the image content to determine the true MIME type of the
// example, due to a misconfigured web server). This approach SHOULD work in // image, and fall back on the provided MIME type if this is not
// all usual cases, but content sniffing could be used in the future to more // possible.
// confidently identify the image type. //
// TODO(crbug.com/943203): Implement content sniffing. // Note that if the type cannot be sniffed AND the provided type is incorrect
AtomicString mime_type = GetResponse().HttpContentType(); // (for example, due to a misconfigured web server), then it is possible that
// either the wrong policy (or no policy) will be enforced. However, this case
// should be exceedingly rare.
String mime_type =
image_->Data() &&
ImageDecoder::HasSufficientDataToSniffImageType(*image_->Data().get())
? ImageDecoder::SniffImageType(image_->Data())
: GetResponse().HttpContentType();
if (MIMETypeRegistry::IsLossyImageMIMEType(mime_type)) { if (MIMETypeRegistry::IsLossyImageMIMEType(mime_type)) {
// Enforce the lossy image policy. // Enforce the lossy image policy.
return context.IsFeatureEnabled( return context.IsFeatureEnabled(
......
...@@ -138,6 +138,31 @@ bool ImageDecoder::HasSufficientDataToSniffImageType(const SharedBuffer& data) { ...@@ -138,6 +138,31 @@ bool ImageDecoder::HasSufficientDataToSniffImageType(const SharedBuffer& data) {
return data.size() >= kLongestSignatureLength; return data.size() >= kLongestSignatureLength;
} }
// static
String ImageDecoder::SniffImageType(scoped_refptr<SharedBuffer> image_data) {
// Access the first kLongestSignatureLength chars to sniff the signature.
// (note: FastSharedBufferReader only makes a copy if the bytes are segmented)
char buffer[kLongestSignatureLength];
const FastSharedBufferReader fast_reader(
SegmentReader::CreateFromSharedBuffer(std::move(image_data)));
const char* contents =
fast_reader.GetConsecutiveData(0, kLongestSignatureLength, buffer);
if (MatchesJPEGSignature(contents))
return "image/jpeg";
if (MatchesPNGSignature(contents))
return "image/png";
if (MatchesGIFSignature(contents))
return "image/gif";
if (MatchesWebPSignature(contents))
return "image/webp";
if (MatchesICOSignature(contents) || MatchesCURSignature(contents))
return "image/x-icon";
if (MatchesBMPSignature(contents))
return "image/bmp";
return String();
}
size_t ImageDecoder::FrameCount() { size_t ImageDecoder::FrameCount() {
const size_t old_size = frame_buffer_cache_.size(); const size_t old_size = frame_buffer_cache_.size();
const size_t new_size = DecodeFrameCount(); const size_t new_size = DecodeFrameCount();
......
...@@ -168,6 +168,9 @@ class PLATFORM_EXPORT ImageDecoder { ...@@ -168,6 +168,9 @@ class PLATFORM_EXPORT ImageDecoder {
// failure is due to insufficient or bad data. // failure is due to insufficient or bad data.
static bool HasSufficientDataToSniffImageType(const SharedBuffer&); static bool HasSufficientDataToSniffImageType(const SharedBuffer&);
// Looks at the image data to determine and return the image MIME type.
static String SniffImageType(scoped_refptr<SharedBuffer> image_data);
void SetData(scoped_refptr<SegmentReader> data, bool all_data_received) { void SetData(scoped_refptr<SegmentReader> data, bool all_data_received) {
if (failed_) if (failed_)
return; return;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment