Merge tag 'v2.5.2' into HEAD

This commit is contained in:
Mike Barnes 2018-10-14 15:14:29 +11:00
commit 7ec79ce03f
16 changed files with 118 additions and 30 deletions

14
CHANGELOG.md Normal file
View file

@ -0,0 +1,14 @@
## 2.5.2
- Fix XSS vulnerability (#8959)
## 2.5.1
- Fix some local images not having their EXIF metadata stripped on upload (#8714)
- Fix class autoloading issue in ActivityPub Create handler (#8820)
- Fix cache statistics not being sent via statsd when statsd enabled (#8831)
- Fix being able to enable a disabled relay via ActivityPub Accept handler (#8864)
- Bump nokogiri from 1.8.4 to 1.8.5 (#8881)
- Bump puma from 3.11.4 to 3.12.0 (#8883)
- Fix database migrations for PostgreSQL below 9.5 (#8903)
- Fix being able to report statuses not belonging to the reported account (#8916)

View file

@ -5,7 +5,7 @@ ruby '>= 2.3.0', '< 2.6.0'
gem 'pkg-config', '~> 1.3'
gem 'puma', '~> 3.11'
gem 'puma', '~> 3.12'
gem 'rails', '~> 5.2.1'
gem 'thor', '~> 0.20'

View file

@ -347,7 +347,7 @@ GEM
net-ssh (>= 2.6.5)
net-ssh (4.2.0)
nio4r (2.3.1)
nokogiri (1.8.4)
nokogiri (1.8.5)
mini_portile2 (~> 2.3.0)
nokogumbo (1.5.0)
nokogiri
@ -412,7 +412,7 @@ GEM
pry-rails (0.3.6)
pry (>= 0.10.4)
public_suffix (3.0.2)
puma (3.11.4)
puma (3.12.0)
pundit (1.1.0)
activesupport (>= 3.0.0)
rack (2.0.5)
@ -721,7 +721,7 @@ DEPENDENCIES
private_address_check (~> 0.4.1)
pry-byebug (~> 3.6)
pry-rails (~> 0.3)
puma (~> 3.11)
puma (~> 3.12)
pundit (~> 1.1)
rack-attack (~> 5.2)
rack-cors (~> 1.0)
@ -765,4 +765,4 @@ RUBY VERSION
ruby 2.5.0p0
BUNDLED WITH
1.16.3
1.16.5

View file

@ -27,7 +27,7 @@ class Api::V1::ReportsController < Api::BaseController
private
def reported_status_ids
Status.find(status_ids).pluck(:id)
reported_account.statuses.find(status_ids).pluck(:id)
end
def status_ids

View file

@ -22,6 +22,12 @@ module SignatureVerification
return
end
if request.headers['Date'].present? && !matches_time_window?
@signature_verification_failure_reason = 'Signed request date outside acceptable time window'
@signed_request_account = nil
return
end
raw_signature = request.headers['Signature']
signature_params = {}
@ -76,7 +82,7 @@ module SignatureVerification
def build_signed_string(signed_headers)
signed_headers = 'date' if signed_headers.blank?
signed_headers.split(' ').map do |signed_header|
signed_headers.downcase.split(' ').map do |signed_header|
if signed_header == Request::REQUEST_TARGET
"#{Request::REQUEST_TARGET}: #{request.method.downcase} #{request.path}"
elsif signed_header == 'digest'
@ -89,12 +95,12 @@ module SignatureVerification
def matches_time_window?
begin
time_sent = DateTime.httpdate(request.headers['Date'])
time_sent = Time.httpdate(request.headers['Date'])
rescue ArgumentError
return false
end
(Time.now.utc - time_sent).abs <= 30
(Time.now.utc - time_sent).abs <= 12.hours
end
def body_digest

View file

@ -26,7 +26,7 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity
end
def relay
@relay ||= Relay.find_by(follow_activity_id: object_uri)
@relay ||= Relay.find_by(follow_activity_id: object_uri) unless object_uri.nil?
end
def relay_follow?

View file

@ -92,7 +92,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
return if tag['href'].blank?
account = account_from_uri(tag['href'])
account = FetchRemoteAccountService.new.call(tag['href'], id: false) if account.nil?
account = ::FetchRemoteAccountService.new.call(tag['href'], id: false) if account.nil?
return if account.nil?
account.mentions.create(status: status)
end

View file

@ -17,6 +17,8 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity
end
def delete_note
return if object_uri.nil?
@status = Status.find_by(uri: object_uri, account: @account)
@status ||= Status.find_by(uri: @object['atomUri'], account: @account) if @object.is_a?(Hash) && @object['atomUri'].present?

View file

@ -28,7 +28,7 @@ class ActivityPub::Activity::Reject < ActivityPub::Activity
end
def relay
@relay ||= Relay.find_by(follow_activity_id: object_uri)
@relay ||= Relay.find_by(follow_activity_id: object_uri) unless object_uri.nil?
end
def relay_follow?

View file

@ -19,6 +19,8 @@ class ActivityPub::Activity::Undo < ActivityPub::Activity
private
def undo_announce
return if object_uri.nil?
status = Status.find_by(uri: object_uri, account: @account)
status ||= Status.find_by(uri: @object['atomUri'], account: @account) if @object.is_a?(Hash) && @object['atomUri'].present?

View file

@ -90,8 +90,12 @@ class Formatter
private
def html_entities
@html_entities ||= HTMLEntities.new
end
def encode(html)
HTMLEntities.new.encode(html)
html_entities.encode(html)
end
def encode_and_link_urls(html, accounts = nil, options = {})
@ -143,7 +147,7 @@ class Formatter
emoji = emoji_map[shortcode]
if emoji
replacement = "<img draggable=\"false\" class=\"emojione\" alt=\":#{shortcode}:\" title=\":#{shortcode}:\" src=\"#{emoji}\" />"
replacement = "<img draggable=\"false\" class=\"emojione\" alt=\":#{encode(shortcode)}:\" title=\":#{encode(shortcode)}:\" src=\"#{encode(emoji)}\" />"
before_html = shortname_start_index.positive? ? html[0..shortname_start_index - 1] : ''
html = before_html + replacement + html[i + 1..-1]
i += replacement.size - (shortcode.size + 2) - 1
@ -212,7 +216,7 @@ class Formatter
return link_to_account(acct) unless linkable_accounts
account = linkable_accounts.find { |item| TagManager.instance.same_acct?(item.acct, acct) }
account ? mention_html(account) : "@#{acct}"
account ? mention_html(account) : "@#{encode(acct)}"
end
def link_to_account(acct)
@ -221,7 +225,7 @@ class Formatter
domain = nil if TagManager.instance.local_domain?(domain)
account = EntityCache.instance.mention(username, domain)
account ? mention_html(account) : "@#{acct}"
account ? mention_html(account) : "@#{encode(acct)}"
end
def link_to_hashtag(entity)
@ -239,10 +243,10 @@ class Formatter
end
def hashtag_html(tag)
"<a href=\"#{tag_url(tag.downcase)}\" class=\"mention hashtag\" rel=\"tag\">#<span>#{tag}</span></a>"
"<a href=\"#{encode(tag_url(tag.downcase))}\" class=\"mention hashtag\" rel=\"tag\">#<span>#{encode(tag)}</span></a>"
end
def mention_html(account)
"<span class=\"h-card\"><a href=\"#{TagManager.instance.url_for(account)}\" class=\"u-url mention\">@<span>#{account.username}</span></a></span>"
"<span class=\"h-card\"><a href=\"#{encode(TagManager.instance.url_for(account))}\" class=\"u-url mention\">@<span>#{encode(account.username)}</span></a></span>"
end
end

View file

@ -9,7 +9,7 @@ if ENV['STATSD_ADDR'].present?
::NSA.inform_statsd(statsd) do |informant|
informant.collect(:action_controller, :web)
informant.collect(:active_record, :db)
informant.collect(:cache, :cache)
informant.collect(:active_support_cache, :cache)
informant.collect(:sidekiq, :sidekiq)
end
end

View file

@ -3,15 +3,10 @@ class CopyStatusStats < ActiveRecord::Migration[5.2]
def up
safety_assured do
Status.unscoped.select('id').find_in_batches(batch_size: 5_000) do |statuses|
execute <<-SQL.squish
INSERT INTO status_stats (status_id, reblogs_count, favourites_count, created_at, updated_at)
SELECT id, reblogs_count, favourites_count, created_at, updated_at
FROM statuses
WHERE id IN (#{statuses.map(&:id).join(', ')})
ON CONFLICT (status_id) DO UPDATE
SET reblogs_count = EXCLUDED.reblogs_count, favourites_count = EXCLUDED.favourites_count
SQL
if supports_upsert?
up_fast
else
up_slow
end
end
end
@ -19,4 +14,41 @@ class CopyStatusStats < ActiveRecord::Migration[5.2]
def down
# Nothing
end
private
def supports_upsert?
version = select_one("SELECT current_setting('server_version_num') AS v")['v'].to_i
version >= 90500
end
def up_fast
say 'Upsert is available, importing counters using the fast method'
Status.unscoped.select('id').find_in_batches(batch_size: 5_000) do |statuses|
execute <<-SQL.squish
INSERT INTO status_stats (status_id, reblogs_count, favourites_count, created_at, updated_at)
SELECT id, reblogs_count, favourites_count, created_at, updated_at
FROM statuses
WHERE id IN (#{statuses.map(&:id).join(', ')})
ON CONFLICT (status_id) DO UPDATE
SET reblogs_count = EXCLUDED.reblogs_count, favourites_count = EXCLUDED.favourites_count
SQL
end
end
def up_slow
say 'Upsert is not available in PostgreSQL below 9.5, falling back to slow import of counters'
# We cannot use bulk INSERT or overarching transactions here because of possible
# uniqueness violations that we need to skip over
Status.unscoped.select('id, reblogs_count, favourites_count, created_at, updated_at').find_each do |status|
begin
params = [[nil, status.id], [nil, status.reblogs_count], [nil, status.favourites_count], [nil, status.created_at], [nil, status.updated_at]]
exec_insert('INSERT INTO status_stats (status_id, reblogs_count, favourites_count, created_at, updated_at) VALUES ($1, $2, $3, $4, $5)', nil, params)
rescue ActiveRecord::RecordNotUnique
next
end
end
end
end

View file

@ -13,7 +13,7 @@ module Mastodon
end
def patch
0
2
end
def pre

View file

@ -20,7 +20,7 @@ module Paperclip
private
def needs_convert?
needs_different_geometry? || needs_different_format?
needs_different_geometry? || needs_different_format? || needs_metadata_stripping?
end
def needs_different_geometry?
@ -31,5 +31,9 @@ module Paperclip
def needs_different_format?
@format.present? && @current_format != @format
end
def needs_metadata_stripping?
@attachment.instance.respond_to?(:local?) && @attachment.instance.local?
end
end
end

View file

@ -73,6 +73,30 @@ describe ApplicationController, type: :controller do
end
end
context 'with request older than a day' do
before do
get :success
fake_request = Request.new(:get, request.url)
fake_request.add_headers({ 'Date' => 2.days.ago.utc.httpdate })
fake_request.on_behalf_of(author)
request.headers.merge!(fake_request.headers)
end
describe '#signed_request?' do
it 'returns true' do
expect(controller.signed_request?).to be true
end
end
describe '#signed_request_account' do
it 'returns nil' do
expect(controller.signed_request_account).to be_nil
end
end
end
context 'with body' do
before do
post :success, body: 'Hello world'