2019-04-30 08:27:42 +08:00
# frozen_string_literal: true
2015-10-11 17:41:23 +08:00
require 'rails_helper'
2013-02-06 03:16:51 +08:00
describe Upload do
2013-06-16 16:39:48 +08:00
2013-07-14 05:42:19 +08:00
let ( :upload ) { build ( :upload ) }
2013-04-07 23:52:46 +08:00
2013-07-14 05:42:19 +08:00
let ( :user_id ) { 1 }
2013-04-07 23:52:46 +08:00
2014-07-14 23:34:23 +08:00
let ( :image_filename ) { " logo.png " }
2014-11-04 02:54:10 +08:00
let ( :image ) { file_from_fixtures ( image_filename ) }
2013-07-14 05:42:19 +08:00
2014-11-04 02:54:10 +08:00
let ( :image_svg_filename ) { " image.svg " }
let ( :image_svg ) { file_from_fixtures ( image_svg_filename ) }
2017-01-12 06:37:12 +08:00
let ( :huge_image_filename ) { " huge.jpg " }
let ( :huge_image ) { file_from_fixtures ( huge_image_filename ) }
2014-04-15 04:55:57 +08:00
let ( :attachment_path ) { __FILE__ }
let ( :attachment ) { File . new ( attachment_path ) }
2013-07-24 06:54:18 +08:00
2013-07-14 05:42:19 +08:00
context " .create_thumbnail! " do
it " does not create a thumbnail when disabled " do
2017-07-07 14:09:14 +08:00
SiteSetting . create_thumbnails = false
2013-08-01 05:26:34 +08:00
OptimizedImage . expects ( :create_for ) . never
2013-09-27 16:55:50 +08:00
upload . create_thumbnail! ( 100 , 100 )
2013-07-14 05:42:19 +08:00
end
it " creates a thumbnail " do
upload = Fabricate ( :upload )
thumbnail = Fabricate ( :optimized_image , upload : upload )
SiteSetting . expects ( :create_thumbnails? ) . returns ( true )
OptimizedImage . expects ( :create_for ) . returns ( thumbnail )
2013-09-27 16:55:50 +08:00
upload . create_thumbnail! ( 100 , 100 )
2013-07-14 05:42:19 +08:00
upload . reload
2014-12-31 22:55:03 +08:00
expect ( upload . optimized_images . count ) . to eq ( 1 )
2013-07-14 05:42:19 +08:00
end
end
2019-02-07 19:09:06 +08:00
it " supports <style> element in SVG " do
SiteSetting . authorized_extensions = " svg "
upload = UploadCreator . new ( image_svg , image_svg_filename ) . create_for ( user_id )
expect ( upload . valid? ) . to eq ( true )
path = Discourse . store . path_for ( upload )
expect ( File . read ( path ) ) . to match ( / <style> / )
end
2018-08-28 10:48:43 +08:00
it " can reconstruct dimensions on demand " do
upload = UploadCreator . new ( huge_image , " image.png " ) . create_for ( user_id )
upload . update_columns ( width : nil , height : nil , thumbnail_width : nil , thumbnail_height : nil )
upload = Upload . find ( upload . id )
2020-09-14 08:10:55 +08:00
expect ( upload . width ) . to eq ( 8900 )
expect ( upload . height ) . to eq ( 8900 )
2018-08-28 10:48:43 +08:00
2018-12-26 23:17:08 +08:00
upload . reload
2020-09-14 08:10:55 +08:00
expect ( upload . read_attribute ( :width ) ) . to eq ( 8900 )
2018-12-26 23:17:08 +08:00
2018-08-28 10:48:43 +08:00
upload . update_columns ( width : nil , height : nil , thumbnail_width : nil , thumbnail_height : nil )
expect ( upload . thumbnail_width ) . to eq ( 500 )
expect ( upload . thumbnail_height ) . to eq ( 500 )
end
2018-12-03 23:19:49 +08:00
it " dimension calculation returns nil on missing image " do
upload = UploadCreator . new ( huge_image , " image.png " ) . create_for ( user_id )
upload . update_columns ( width : nil , height : nil , thumbnail_width : nil , thumbnail_height : nil )
missing_url = " wrong_folder #{ upload . url } "
upload . update_columns ( url : missing_url )
expect ( upload . thumbnail_height ) . to eq ( nil )
expect ( upload . thumbnail_width ) . to eq ( nil )
end
2020-09-15 07:22:57 +08:00
it 'returns error when image resolution is to big' do
begin
SiteSetting . max_image_megapixels = 10
upload = UploadCreator . new ( huge_image , " image.png " ) . create_for ( user_id )
expect ( upload . id ) . to be_nil
expect ( upload . errors . messages [ :base ] . first ) . to eq ( " Sorry, the image you are trying to upload is too large (maximum dimension is 20-megapixels), please resize it and try again. " )
ensure
SiteSetting . max_image_megapixels = 40
end
end
2017-07-04 23:50:08 +08:00
it " extracts file extension " do
created_upload = UploadCreator . new ( image , image_filename ) . create_for ( user_id )
expect ( created_upload . extension ) . to eq ( " png " )
end
2017-12-14 04:51:09 +08:00
it " should create an invalid upload when the filename is blank " do
SiteSetting . authorized_extensions = " * "
2018-08-20 10:41:46 +08:00
created_upload = UploadCreator . new ( attachment , nil ) . create_for ( user_id )
2017-12-14 04:51:09 +08:00
expect ( created_upload . valid? ) . to eq ( false )
end
2019-04-09 04:55:26 +08:00
context " .extract_url " do
let ( :url ) { 'https://example.com/uploads/default/original/1X/d1c2d40ab994e8410c.png' }
it 'should return the right part of url' do
expect ( Upload . extract_url ( url ) . to_s ) . to eq ( '/original/1X/d1c2d40ab994e8410c.png' )
end
end
2013-07-14 05:42:19 +08:00
context " .get_from_url " do
2018-09-06 14:29:45 +08:00
let ( :sha1 ) { " 10f73034616a796dfd70177dc54b6def44c4ba6f " }
2018-09-14 13:42:59 +08:00
let ( :upload ) { Fabricate ( :upload , sha1 : sha1 ) }
2013-07-14 05:42:19 +08:00
2013-07-22 06:37:23 +08:00
it " works when the file has been uploaded " do
2016-10-18 15:58:45 +08:00
expect ( Upload . get_from_url ( upload . url ) ) . to eq ( upload )
2013-07-22 06:37:23 +08:00
end
2018-09-12 15:12:14 +08:00
describe 'for an extensionless url' do
2018-09-14 13:42:59 +08:00
before do
upload . update! ( url : upload . url . sub ( '.png' , '' ) )
upload . reload
end
2018-09-12 15:12:14 +08:00
it 'should return the right upload' do
expect ( Upload . get_from_url ( upload . url ) ) . to eq ( upload )
end
end
2019-04-24 10:15:47 +08:00
it " should return the right upload as long as the upload's URL matches " do
upload . update! ( url : " /uploads/default/12345/971308e535305c51.png " )
expect ( Upload . get_from_url ( upload . url ) ) . to eq ( upload )
2019-04-24 10:20:42 +08:00
expect ( Upload . get_from_url ( " /uploads/default/123131/971308e535305c51.png " ) )
. to eq ( nil )
2019-04-24 10:15:47 +08:00
end
2018-09-14 13:42:59 +08:00
describe 'for a url a tree' do
before do
upload . update! ( url :
Discourse . store . get_path_for (
" original " ,
16001 ,
upload . sha1 ,
" . #{ upload . extension } "
)
)
end
2018-09-12 14:48:25 +08:00
it 'should return the right upload' do
expect ( Upload . get_from_url ( upload . url ) ) . to eq ( upload )
end
end
2013-07-22 06:37:23 +08:00
it " works when using a cdn " do
2016-10-18 13:39:16 +08:00
begin
original_asset_host = Rails . configuration . action_controller . asset_host
Rails . configuration . action_controller . asset_host = 'http://my.cdn.com'
2016-10-18 15:58:45 +08:00
expect ( Upload . get_from_url (
URI . join ( " http://my.cdn.com " , upload . url ) . to_s
) ) . to eq ( upload )
2016-10-18 13:39:16 +08:00
ensure
Rails . configuration . action_controller . asset_host = original_asset_host
end
2013-07-22 06:37:23 +08:00
end
2016-10-18 15:58:45 +08:00
it " should return the right upload when using the full URL " do
expect ( Upload . get_from_url (
URI . join ( " http://discourse.some.com:3000/ " , upload . url ) . to_s
) ) . to eq ( upload )
end
2016-10-20 18:34:42 +08:00
it " doesn't blow up with an invalid URI " do
expect { Upload . get_from_url ( " http://ip:port/index.html " ) } . not_to raise_error
2018-05-17 15:13:30 +08:00
expect { Upload . get_from_url ( " mailto:admin%40example.com " ) } . not_to raise_error
2018-05-18 20:31:36 +08:00
expect { Upload . get_from_url ( " mailto:example " ) } . not_to raise_error
2016-10-20 18:34:42 +08:00
end
2016-10-18 15:58:45 +08:00
describe " s3 store " do
2018-09-14 13:42:59 +08:00
let ( :upload ) { Fabricate ( :upload_s3 ) }
let ( :path ) { upload . url . sub ( SiteSetting . Upload . s3_base_url , '' ) }
2016-10-18 15:58:45 +08:00
before do
SiteSetting . enable_s3_uploads = true
SiteSetting . s3_upload_bucket = " s3-upload-bucket "
SiteSetting . s3_access_key_id = " some key "
SiteSetting . s3_secret_access_key = " some secret key "
end
2018-06-05 21:19:06 +08:00
it " should return the right upload when using base url (not CDN) for s3 " do
upload
2018-09-14 13:42:59 +08:00
expect ( Upload . get_from_url ( upload . url ) ) . to eq ( upload )
2018-06-05 21:19:06 +08:00
end
2018-07-06 11:36:29 +08:00
describe 'when using a cdn' do
let ( :s3_cdn_url ) { 'https://mycdn.slowly.net' }
before do
SiteSetting . s3_cdn_url = s3_cdn_url
end
2016-10-18 15:58:45 +08:00
2018-07-06 11:36:29 +08:00
it " should return the right upload " do
upload
expect ( Upload . get_from_url ( URI . join ( s3_cdn_url , path ) . to_s ) ) . to eq ( upload )
end
describe 'when upload bucket contains subfolder' do
before do
SiteSetting . s3_upload_bucket = " s3-upload-bucket/path/path2 "
end
it " should return the right upload " do
upload
expect ( Upload . get_from_url ( URI . join ( s3_cdn_url , path ) . to_s ) ) . to eq ( upload )
end
end
2016-10-18 15:58:45 +08:00
end
2018-01-12 11:08:15 +08:00
it " should return the right upload when using one CDN for both s3 and assets " do
begin
original_asset_host = Rails . configuration . action_controller . asset_host
cdn_url = 'http://my.cdn.com'
Rails . configuration . action_controller . asset_host = cdn_url
SiteSetting . s3_cdn_url = cdn_url
upload
expect ( Upload . get_from_url (
URI . join ( cdn_url , path ) . to_s
) ) . to eq ( upload )
ensure
Rails . configuration . action_controller . asset_host = original_asset_host
end
end
2016-10-18 15:58:45 +08:00
end
2013-07-14 05:42:19 +08:00
end
2016-09-02 14:50:13 +08:00
describe '.generate_digest' do
it " should return the right digest " do
expect ( Upload . generate_digest ( image . path ) ) . to eq ( 'bc975735dfc6409c1c2aa5ebf2239949bcbdbd65' )
end
end
2017-08-22 23:46:15 +08:00
describe '.short_url' do
it " should generate a correct short url " do
upload = Upload . new ( sha1 : 'bda2c513e1da04f7b4e99230851ea2aafeb8cc4e' , extension : 'png' )
expect ( upload . short_url ) . to eq ( 'upload://r3AYqESanERjladb4vBB7VsMBm6.png' )
2019-06-19 09:10:50 +08:00
upload . extension = nil
expect ( upload . short_url ) . to eq ( 'upload://r3AYqESanERjladb4vBB7VsMBm6' )
2017-08-22 23:46:15 +08:00
end
end
describe '.sha1_from_short_url' do
it " should be able to look up sha1 " do
sha1 = 'bda2c513e1da04f7b4e99230851ea2aafeb8cc4e'
expect ( Upload . sha1_from_short_url ( 'upload://r3AYqESanERjladb4vBB7VsMBm6.png' ) ) . to eq ( sha1 )
expect ( Upload . sha1_from_short_url ( 'upload://r3AYqESanERjladb4vBB7VsMBm6' ) ) . to eq ( sha1 )
expect ( Upload . sha1_from_short_url ( 'r3AYqESanERjladb4vBB7VsMBm6' ) ) . to eq ( sha1 )
end
2017-08-23 23:08:18 +08:00
it " should be able to look up sha1 even with leading zeros " do
sha1 = '0000c513e1da04f7b4e99230851ea2aafeb8cc4e'
expect ( Upload . sha1_from_short_url ( 'upload://1Eg9p8rrCURq4T3a6iJUk0ri6.png' ) ) . to eq ( sha1 )
end
2017-08-22 23:46:15 +08:00
end
2019-06-04 14:10:46 +08:00
describe '#base62_sha1' do
it 'should return the right value' do
upload . update! ( sha1 : " 0000c513e1da04f7b4e99230851ea2aafeb8cc4e " )
expect ( upload . base62_sha1 ) . to eq ( " 1Eg9p8rrCURq4T3a6iJUk0ri6 " )
end
end
2019-05-29 09:00:25 +08:00
describe '.sha1_from_short_path' do
it " should be able to lookup sha1 " do
path = " /uploads/short-url/3UjQ4jHoyeoQndk5y3qHzm3QVTQ.png "
sha1 = " 1b6453892473a467d07372d45eb05abc2031647a "
expect ( Upload . sha1_from_short_path ( path ) ) . to eq ( sha1 )
expect ( Upload . sha1_from_short_path ( path . sub ( " .png " , " " ) ) ) . to eq ( sha1 )
end
end
2018-11-14 15:03:02 +08:00
describe '#to_s' do
it 'should return the right value' do
expect ( upload . to_s ) . to eq ( upload . url )
end
end
2019-03-14 12:38:16 +08:00
describe '.migrate_to_new_scheme' do
it 'should not migrate system uploads' do
SiteSetting . migrate_to_new_scheme = true
expect { Upload . migrate_to_new_scheme }
. to_not change { Upload . pluck ( :url ) }
end
end
2019-11-18 09:25:42 +08:00
describe '.update_secure_status' do
2019-11-28 05:32:17 +08:00
it " respects the secure_override_value parameter if provided " do
upload . update! ( secure : true )
upload . update_secure_status ( secure_override_value : true )
expect ( upload . secure ) . to eq ( true )
upload . update_secure_status ( secure_override_value : false )
expect ( upload . secure ) . to eq ( false )
end
2019-11-18 09:25:42 +08:00
it 'marks a local upload as not secure with default settings' do
upload . update! ( secure : true )
expect { upload . update_secure_status }
. to change { upload . secure }
expect ( upload . secure ) . to eq ( false )
end
2020-03-26 05:16:02 +08:00
it 'marks a local attachment as secure if secure media enabled' do
2019-11-18 09:25:42 +08:00
SiteSetting . authorized_extensions = " pdf "
2020-02-21 07:35:16 +08:00
upload . update! ( original_filename : " small.pdf " , extension : " pdf " , secure : false , access_control_post : Fabricate ( :private_message_post ) )
enable_secure_media
2019-11-18 09:25:42 +08:00
expect { upload . update_secure_status }
. to change { upload . secure }
expect ( upload . secure ) . to eq ( true )
end
2020-03-26 05:16:02 +08:00
it 'marks a local attachment as not secure if secure media enabled' do
2019-11-18 09:25:42 +08:00
SiteSetting . authorized_extensions = " pdf "
upload . update! ( original_filename : " small.pdf " , extension : " pdf " , secure : true )
expect { upload . update_secure_status }
. to change { upload . secure }
expect ( upload . secure ) . to eq ( false )
end
2020-03-26 05:16:02 +08:00
it 'does not change secure status of a non-attachment when prevent_anons_from_downloading_files is enabled by itself' do
2019-11-18 09:25:42 +08:00
SiteSetting . prevent_anons_from_downloading_files = true
SiteSetting . authorized_extensions = " mp4 "
upload . update! ( original_filename : " small.mp4 " , extension : " mp4 " )
expect { upload . update_secure_status }
. not_to change { upload . secure }
expect ( upload . secure ) . to eq ( false )
end
context " secure media enabled " do
before do
2020-01-29 08:11:38 +08:00
enable_secure_media
2019-11-18 09:25:42 +08:00
end
2020-01-16 11:50:27 +08:00
it 'does not mark an image upload as not secure when there is no access control post id, to avoid unintentional exposure' do
2019-11-18 09:25:42 +08:00
upload . update! ( secure : true )
2020-01-16 11:50:27 +08:00
upload . update_secure_status
expect ( upload . secure ) . to eq ( true )
end
2019-11-18 09:25:42 +08:00
2020-01-16 11:50:27 +08:00
it 'marks the upload as not secure if its access control post is a public post' do
upload . update! ( secure : true , access_control_post : Fabricate ( :post ) )
upload . update_secure_status
2019-11-18 09:25:42 +08:00
expect ( upload . secure ) . to eq ( false )
end
2020-01-16 11:50:27 +08:00
it 'leaves the upload as secure if its access control post is a PM post' do
upload . update! ( secure : true , access_control_post : Fabricate ( :private_message_post ) )
upload . update_secure_status
expect ( upload . secure ) . to eq ( true )
end
2019-11-18 09:25:42 +08:00
it 'marks an image upload as secure if login_required is enabled' do
SiteSetting . login_required = true
upload . update! ( secure : false )
expect { upload . update_secure_status }
. to change { upload . secure }
2020-02-14 09:17:09 +08:00
expect ( upload . reload . secure ) . to eq ( true )
end
it 'does not mark an upload used for a custom emoji as secure' do
SiteSetting . login_required = true
upload . update! ( secure : false )
CustomEmoji . create ( name : 'meme' , upload : upload )
2020-02-18 13:08:58 +08:00
upload . update_secure_status
2020-02-14 09:17:09 +08:00
expect ( upload . reload . secure ) . to eq ( false )
2019-11-18 09:25:42 +08:00
end
2020-02-17 10:30:47 +08:00
it 'does not mark an upload whose origin matches a regular emoji as secure (sometimes emojis are downloaded in pull_hotlinked_images)' do
SiteSetting . login_required = true
2020-02-18 13:13:34 +08:00
falafel = Emoji . all . find { | e | e . url == '/images/emoji/twitter/falafel.png?v=9' }
2020-02-18 13:08:58 +08:00
upload . update! ( secure : false , origin : " http://localhost:3000 #{ falafel . url } " )
upload . update_secure_status
2020-02-17 13:11:15 +08:00
expect ( upload . reload . secure ) . to eq ( false )
end
it 'does not mark any upload with origin containing images/emoji in the URL' do
SiteSetting . login_required = true
upload . update! ( secure : false , origin : " http://localhost:3000/images/emoji/test.png " )
2020-02-18 13:08:58 +08:00
upload . update_secure_status
2020-02-17 13:11:15 +08:00
expect ( upload . reload . secure ) . to eq ( false )
2020-02-17 10:30:47 +08:00
end
2019-11-18 09:25:42 +08:00
end
end
2020-01-29 08:11:38 +08:00
def enable_secure_media
SiteSetting . enable_s3_uploads = true
SiteSetting . s3_upload_bucket = " s3-upload-bucket "
SiteSetting . s3_access_key_id = " some key "
SiteSetting . s3_secret_access_key = " some secrets3_region key "
SiteSetting . secure_media = true
stub_request ( :head , " https:// #{ SiteSetting . s3_upload_bucket } .s3.amazonaws.com/ " )
stub_request (
:put ,
" https:// #{ SiteSetting . s3_upload_bucket } .s3.amazonaws.com/original/1X/ #{ upload . sha1 } . #{ upload . extension } ?acl "
)
end
2020-08-18 08:55:16 +08:00
context '.destroy' do
it " can correctly clear information when destroying an upload " do
upload = Fabricate ( :upload )
user = Fabricate ( :user )
user . user_profile . update! (
card_background_upload_id : upload . id ,
profile_background_upload_id : upload . id
)
upload . destroy
user . user_profile . reload
expect ( user . user_profile . card_background_upload_id ) . to eq ( nil )
expect ( user . user_profile . profile_background_upload_id ) . to eq ( nil )
end
2020-08-28 09:28:11 +08:00
end
context " .signed_url_from_secure_media_url " do
before do
# must be done so signed_url_for_path exists
enable_secure_media
end
it " correctly gives back a signed url from a path only " do
secure_url = " /secure-media-uploads/original/1X/c5a2c4ba0fa390f5aac5c2c1a12416791ebdd9e9.png "
signed_url = Upload . signed_url_from_secure_media_url ( secure_url )
expect ( signed_url ) . not_to include ( " secure-media-uploads " )
expect ( UrlHelper . s3_presigned_url? ( signed_url ) ) . to eq ( true )
end
it " correctly gives back a signed url from a full url " do
secure_url = " http://localhost:3000/secure-media-uploads/original/1X/c5a2c4ba0fa390f5aac5c2c1a12416791ebdd9e9.png "
signed_url = Upload . signed_url_from_secure_media_url ( secure_url )
expect ( signed_url ) . not_to include ( Discourse . base_url )
expect ( UrlHelper . s3_presigned_url? ( signed_url ) ) . to eq ( true )
end
end
context " .secure_media_url_from_upload_url " do
before do
# must be done so signed_url_for_path exists
enable_secure_media
end
it " gets the secure media url from an S3 upload url " do
upload = Fabricate ( :upload_s3 , secure : true )
url = upload . url
secure_url = Upload . secure_media_url_from_upload_url ( url )
expect ( secure_url ) . not_to include ( SiteSetting . Upload . absolute_base_url )
end
end
context " .secure_media_url? " do
it " works for a secure media url with or without schema + host " do
url = " //localhost:3000/secure-media-uploads/original/2X/f/f62055931bb702c7fd8f552fb901f977e0289a18.png "
expect ( Upload . secure_media_url? ( url ) ) . to eq ( true )
url = " /secure-media-uploads/original/2X/f/f62055931bb702c7fd8f552fb901f977e0289a18.png "
expect ( Upload . secure_media_url? ( url ) ) . to eq ( true )
url = " http://localhost:3000/secure-media-uploads/original/2X/f/f62055931bb702c7fd8f552fb901f977e0289a18.png "
expect ( Upload . secure_media_url? ( url ) ) . to eq ( true )
end
2020-08-18 08:55:16 +08:00
2020-08-28 09:28:11 +08:00
it " does not get false positives on a topic url " do
url = " /t/secure-media-uploads-are-cool/42839 "
expect ( Upload . secure_media_url? ( url ) ) . to eq ( false )
end
it " returns true only for secure media URL for actual media (images/video/audio) " do
url = " /secure-media-uploads/original/2X/f/f62055931bb702c7fd8f552fb901f977e0289a18.mp4 "
expect ( Upload . secure_media_url? ( url ) ) . to eq ( true )
url = " /secure-media-uploads/original/2X/f/f62055931bb702c7fd8f552fb901f977e0289a18.png "
expect ( Upload . secure_media_url? ( url ) ) . to eq ( true )
url = " /secure-media-uploads/original/2X/f/f62055931bb702c7fd8f552fb901f977e0289a18.mp3 "
expect ( Upload . secure_media_url? ( url ) ) . to eq ( true )
url = " /secure-media-uploads/original/2X/f/f62055931bb702c7fd8f552fb901f977e0289a18.pdf "
expect ( Upload . secure_media_url? ( url ) ) . to eq ( false )
end
it " does not work for regular upload urls " do
url = " /uploads/default/test_0/original/1X/e1864389d8252958586c76d747b069e9f68827e3.png "
expect ( Upload . secure_media_url? ( url ) ) . to eq ( false )
end
2020-08-18 08:55:16 +08:00
end
2013-02-06 03:16:51 +08:00
end