Fix #3804, Fix #5776master^2
@@ -2,10 +2,6 @@ | |||||
module Admin | module Admin | ||||
class CustomEmojisController < BaseController | class CustomEmojisController < BaseController | ||||
include ObfuscateFilename | |||||
obfuscate_filename [:custom_emoji, :image] | |||||
def index | def index | ||||
authorize :custom_emoji, :index? | authorize :custom_emoji, :index? | ||||
@@ -4,9 +4,6 @@ class Api::V1::MediaController < Api::BaseController | |||||
before_action -> { doorkeeper_authorize! :write, :'write:media' } | before_action -> { doorkeeper_authorize! :write, :'write:media' } | ||||
before_action :require_user! | before_action :require_user! | ||||
include ObfuscateFilename | |||||
obfuscate_filename :file | |||||
respond_to :json | respond_to :json | ||||
def create | def create | ||||
@@ -24,6 +24,7 @@ class ApplicationController < ActionController::Base | |||||
rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity | rescue_from ActionController::InvalidAuthenticityToken, with: :unprocessable_entity | ||||
rescue_from ActionController::UnknownFormat, with: :not_acceptable | rescue_from ActionController::UnknownFormat, with: :not_acceptable | ||||
rescue_from ActionController::ParameterMissing, with: :bad_request | rescue_from ActionController::ParameterMissing, with: :bad_request | ||||
rescue_from Paperclip::AdapterRegistry::NoHandlerError, with: :bad_request | |||||
rescue_from ActiveRecord::RecordNotFound, with: :not_found | rescue_from ActiveRecord::RecordNotFound, with: :not_found | ||||
rescue_from Mastodon::NotPermittedError, with: :forbidden | rescue_from Mastodon::NotPermittedError, with: :forbidden | ||||
rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error | rescue_from HTTP::Error, OpenSSL::SSL::SSLError, with: :internal_server_error | ||||
@@ -1,16 +0,0 @@ | |||||
# frozen_string_literal: true | |||||
module ObfuscateFilename | |||||
extend ActiveSupport::Concern | |||||
class_methods do | |||||
def obfuscate_filename(path) | |||||
before_action do | |||||
file = params.dig(*path) | |||||
next if file.nil? | |||||
file.original_filename = SecureRandom.hex(8) + File.extname(file.original_filename) | |||||
end | |||||
end | |||||
end | |||||
end |
@@ -1,16 +1,11 @@ | |||||
# frozen_string_literal: true | # frozen_string_literal: true | ||||
class Settings::ProfilesController < Settings::BaseController | class Settings::ProfilesController < Settings::BaseController | ||||
include ObfuscateFilename | |||||
layout 'admin' | layout 'admin' | ||||
before_action :authenticate_user! | before_action :authenticate_user! | ||||
before_action :set_account | before_action :set_account | ||||
obfuscate_filename [:account, :avatar] | |||||
obfuscate_filename [:account, :header] | |||||
def show | def show | ||||
@account.build_fields | @account.build_fields | ||||
end | end | ||||
@@ -9,6 +9,7 @@ module Attachmentable | |||||
GIF_MATRIX_LIMIT = 921_600 # 1280x720px | GIF_MATRIX_LIMIT = 921_600 # 1280x720px | ||||
included do | included do | ||||
before_post_process :obfuscate_file_name | |||||
before_post_process :set_file_extensions | before_post_process :set_file_extensions | ||||
before_post_process :check_image_dimensions | before_post_process :check_image_dimensions | ||||
before_post_process :set_file_content_type | before_post_process :set_file_content_type | ||||
@@ -68,4 +69,14 @@ module Attachmentable | |||||
rescue Terrapin::CommandLineError | rescue Terrapin::CommandLineError | ||||
'' | '' | ||||
end | end | ||||
def obfuscate_file_name | |||||
self.class.attachment_definitions.each_key do |attachment_name| | |||||
attachment = send(attachment_name) | |||||
next if attachment.blank? | |||||
attachment.instance_write :file_name, SecureRandom.hex(8) + File.extname(attachment.instance_read(:file_name)) | |||||
end | |||||
end | |||||
end | end |
@@ -202,9 +202,12 @@ class MediaAttachment < ApplicationRecord | |||||
end | end | ||||
after_commit :reset_parent_cache, on: :update | after_commit :reset_parent_cache, on: :update | ||||
before_create :prepare_description, unless: :local? | before_create :prepare_description, unless: :local? | ||||
before_create :set_shortcode | before_create :set_shortcode | ||||
before_post_process :set_type_and_extension | before_post_process :set_type_and_extension | ||||
before_save :set_meta | before_save :set_meta | ||||
class << self | class << self | ||||
@@ -1,5 +1,7 @@ | |||||
# frozen_string_literal: true | # frozen_string_literal: true | ||||
Paperclip::DataUriAdapter.register | |||||
Paperclip.interpolates :filename do |attachment, style| | Paperclip.interpolates :filename do |attachment, style| | ||||
if style == :original | if style == :original | ||||
attachment.original_filename | attachment.original_filename | ||||
@@ -85,10 +85,7 @@ describe Api::ProofsController do | |||||
end | end | ||||
it 'has the correct avatar url' do | it 'has the correct avatar url' do | ||||
first_part = 'https://cb6e6126.ngrok.io/system/accounts/avatars/' | |||||
last_part = 'original/avatar.gif' | |||||
expect(body_as_json[:avatar]).to match /#{Regexp.quote(first_part)}(?:\d{3,5}\/){3}#{Regexp.quote(last_part)}/ | |||||
expect(body_as_json[:avatar]).to match "https://cb6e6126.ngrok.io#{alice.avatar.url}" | |||||
end | end | ||||
end | end | ||||
end | end | ||||
@@ -1,30 +0,0 @@ | |||||
# frozen_string_literal: true | |||||
require 'rails_helper' | |||||
describe ApplicationController, type: :controller do | |||||
controller do | |||||
include ObfuscateFilename | |||||
obfuscate_filename :file | |||||
def file | |||||
render plain: params[:file]&.original_filename | |||||
end | |||||
end | |||||
before do | |||||
routes.draw { get 'file' => 'anonymous#file' } | |||||
end | |||||
it 'obfusticates filename if the given parameter is specified' do | |||||
file = fixture_file_upload('files/imports.txt', 'text/plain') | |||||
post 'file', params: { file: file } | |||||
expect(response.body).to end_with '.txt' | |||||
expect(response.body).not_to include 'imports' | |||||
end | |||||
it 'does nothing if the given parameter is not specified' do | |||||
post 'file' | |||||
end | |||||
end |
@@ -823,4 +823,5 @@ RSpec.describe Account, type: :model do | |||||
end | end | ||||
include_examples 'AccountAvatar', :account | include_examples 'AccountAvatar', :account | ||||
include_examples 'AccountHeader', :account | |||||
end | end |
@@ -133,6 +133,24 @@ RSpec.describe MediaAttachment, type: :model do | |||||
expect(media.file.meta["small"]["height"]).to eq 327 | expect(media.file.meta["small"]["height"]).to eq 327 | ||||
expect(media.file.meta["small"]["aspect"]).to eq 490.0 / 327 | expect(media.file.meta["small"]["aspect"]).to eq 490.0 / 327 | ||||
end | end | ||||
it 'gives the file a random name' do | |||||
expect(media.file_file_name).to_not eq 'attachment.jpg' | |||||
end | |||||
end | |||||
describe 'base64-encoded jpeg' do | |||||
let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" } | |||||
let(:media) { MediaAttachment.create(account: Fabricate(:account), file: base64_attachment) } | |||||
it 'saves media attachment' do | |||||
expect(media.persisted?).to be true | |||||
expect(media.file).to_not be_nil | |||||
end | |||||
it 'gives the file a file name' do | |||||
expect(media.file_file_name).to_not be_blank | |||||
end | |||||
end | end | ||||
describe 'descriptions for remote attachments' do | describe 'descriptions for remote attachments' do | ||||
@@ -16,4 +16,24 @@ shared_examples 'AccountAvatar' do |fabricator| | |||||
end | end | ||||
end | end | ||||
end | end | ||||
describe 'base64-encoded files' do | |||||
let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" } | |||||
let(:account) { Fabricate(fabricator, avatar: base64_attachment) } | |||||
it 'saves avatar' do | |||||
expect(account.persisted?).to be true | |||||
expect(account.avatar).to_not be_nil | |||||
end | |||||
it 'gives the avatar a file name' do | |||||
expect(account.avatar_file_name).to_not be_blank | |||||
end | |||||
it 'saves a new avatar under a different file name' do | |||||
previous_file_name = account.avatar_file_name | |||||
account.update(avatar: base64_attachment) | |||||
expect(account.avatar_file_name).to_not eq previous_file_name | |||||
end | |||||
end | |||||
end | end |
@@ -0,0 +1,23 @@ | |||||
# frozen_string_literal: true | |||||
shared_examples 'AccountHeader' do |fabricator| | |||||
describe 'base64-encoded files' do | |||||
let(:base64_attachment) { "data:image/jpeg;base64,#{Base64.encode64(attachment_fixture('attachment.jpg').read)}" } | |||||
let(:account) { Fabricate(fabricator, header: base64_attachment) } | |||||
it 'saves header' do | |||||
expect(account.persisted?).to be true | |||||
expect(account.header).to_not be_nil | |||||
end | |||||
it 'gives the header a file name' do | |||||
expect(account.header_file_name).to_not be_blank | |||||
end | |||||
it 'saves a new header under a different file name' do | |||||
previous_file_name = account.header_file_name | |||||
account.update(header: base64_attachment) | |||||
expect(account.header_file_name).to_not eq previous_file_name | |||||
end | |||||
end | |||||
end |