require 'rails_helper' RSpec.describe Preview::ThumbnailFetcher do describe '.fetch' do it 'accepts svg thumbnails for the common safe rasterisation path' do page = Preview::HttpFetcher::Response.new( '', 'text/html', 'https://example.com/page') svg = Preview::HttpFetcher::Response.new( '', 'image/svg+xml', 'https://example.com/thumb.svg') allow(Preview::UrlSafety).to receive(:validate) do |url| [URI.parse(url), ['203.0.113.10']] end allow(Preview::HttpFetcher).to receive(:fetch) .with('https://example.com/page', max_bytes: described_class::HTML_MAX_BYTES) .and_return(page) allow(Preview::HttpFetcher).to receive(:fetch) .with('https://example.com/thumb.svg') .and_return(svg) expect(described_class.fetch('https://example.com/page')).to eq('') end it 'accepts allowed image content type with parameters' do page = Preview::HttpFetcher::Response.new( '', 'text/html', 'https://example.com/page') image = Preview::HttpFetcher::Response.new( "\xFF\xD8\xFFjpeg-bytes".b, 'image/jpeg; charset=binary', 'https://example.com/thumb.jpg') allow(Preview::UrlSafety).to receive(:validate) do |url| [URI.parse(url), ['203.0.113.10']] end allow(Preview::HttpFetcher).to receive(:fetch) .with('https://example.com/page', max_bytes: described_class::HTML_MAX_BYTES) .and_return(page) allow(Preview::HttpFetcher).to receive(:fetch) .with('https://example.com/thumb.jpg') .and_return(image) expect(described_class.fetch('https://example.com/page')) .to eq("\xFF\xD8\xFFjpeg-bytes".b) end end describe '.fetch_image_response' do it 'rejects an unsafe input URL before HTTP fetch' do allow(Preview::UrlSafety).to receive(:validate) .and_raise(Preview::UrlSafety::UnsafeUrl, '安全でない接続先は使用できません.') expect(Preview::HttpFetcher).not_to receive(:fetch) expect { described_class.fetch_image_response('https://unsafe.example.com/thumb.jpg') }.to raise_error(Preview::UrlSafety::UnsafeUrl, '安全でない接続先は使用できません.') end it 'rejects an unsafe redirect target' do allow(Preview::UrlSafety).to receive(:validate) .and_return([URI.parse('https://example.com/thumb.jpg'), ['8.8.8.8']]) allow(Preview::HttpFetcher).to receive(:fetch) .with('https://example.com/thumb.jpg') .and_raise(Preview::UrlSafety::UnsafeUrl, '安全でない接続先は使用できません.') expect { described_class.fetch_image_response('https://example.com/thumb.jpg') }.to raise_error(Preview::UrlSafety::UnsafeUrl, '安全でない接続先は使用できません.') end it 'rejects an oversized image response' do allow(Preview::UrlSafety).to receive(:validate) .and_return([URI.parse('https://example.com/thumb.jpg'), ['8.8.8.8']]) allow(Preview::HttpFetcher).to receive(:fetch) .with('https://example.com/thumb.jpg') .and_raise(Preview::HttpFetcher::ResponseTooLarge, 'too large') expect { described_class.fetch_image_response('https://example.com/thumb.jpg') }.to raise_error(Preview::HttpFetcher::ResponseTooLarge) end it 'rejects a non-image content type' do allow(Preview::UrlSafety).to receive(:validate) .and_return([URI.parse('https://example.com/thumb.jpg'), ['8.8.8.8']]) allow(Preview::HttpFetcher).to receive(:fetch) .with('https://example.com/thumb.jpg') .and_return( Preview::HttpFetcher::Response.new( '', 'text/html', 'https://example.com/thumb.jpg')) expect { described_class.fetch_image_response('https://example.com/thumb.jpg') }.to raise_error(Preview::ThumbnailFetcher::GenerationFailed) end end end