diff --git a/.env.production.sample b/.env.production.sample index 6d9929f70..d51144d96 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -2,7 +2,7 @@ # with the `rake mastodon:setup` interactive setup wizard, but to customize # your setup even further, you'll need to edit it manually. This sample does # not demonstrate all available configuration options. Please look at -# https://docs.joinmastodon/admin/config/ for the full documentation. +# https://docs.joinmastodon.org/admin/config/ for the full documentation. # Federation # ---------- diff --git a/.eslintrc.js b/.eslintrc.js index 177496d3a..7dda01108 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -199,6 +199,11 @@ module.exports = { 'import/no-unresolved': 'error', 'import/no-webpack-loader-syntax': 'error', - 'promise/catch-or-return': 'error', + 'promise/catch-or-return': [ + 'error', + { + allowFinally: true, + }, + ], }, }; diff --git a/.rubocop.yml b/.rubocop.yml index 3a11f7000..25e0fa940 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -28,6 +28,10 @@ Layout/EmptyLineAfterMagicComment: Layout/SpaceInsideHashLiteralBraces: EnforcedStyle: space +Lint/UselessAccessModifier: + ContextCreatingMethods: + - class_methods + Metrics/AbcSize: Max: 100 diff --git a/CHANGELOG.md b/CHANGELOG.md index 7d0110936..6296f0016 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ Changelog All notable changes to this project will be documented in this file. +## [v3.1.5] - 2020-07-07 +### Security + +- Fix media attachment enumeration ([ThibG](https://github.com/tootsuite/mastodon/pull/14254)) +- Change rate limits for various paths ([Gargron](https://github.com/tootsuite/mastodon/pull/14253)) +- Fix other sessions not being logged out on password change ([Gargron](https://github.com/tootsuite/mastodon/pull/14252)) + ## [v3.1.4] - 2020-05-14 ### Added diff --git a/app/chewy/statuses_index.rb b/app/chewy/statuses_index.rb index d4b05fca9..47cb856ea 100644 --- a/app/chewy/statuses_index.rb +++ b/app/chewy/statuses_index.rb @@ -31,7 +31,7 @@ class StatusesIndex < Chewy::Index }, } - define_type ::Status.unscoped.kept.without_reblogs.includes(:media_attachments), delete_if: ->(status) { status.searchable_by.empty? } do + define_type ::Status.unscoped.kept.without_reblogs.includes(:media_attachments, :preloadable_poll) do crutch :mentions do |collection| data = ::Mention.where(status_id: collection.map(&:id)).where(account: Account.local, silent: false).pluck(:status_id, :account_id) data.each.with_object({}) { |(id, name), result| (result[id] ||= []).push(name) } diff --git a/app/controllers/api/v1/statuses/reblogs_controller.rb b/app/controllers/api/v1/statuses/reblogs_controller.rb index 7fa774a4d..1be15a5a4 100644 --- a/app/controllers/api/v1/statuses/reblogs_controller.rb +++ b/app/controllers/api/v1/statuses/reblogs_controller.rb @@ -5,7 +5,7 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController before_action -> { doorkeeper_authorize! :write, :'write:statuses' } before_action :require_user! - before_action :set_reblog + before_action :set_reblog, only: [:create] override_rate_limit_headers :create, family: :statuses @@ -16,15 +16,21 @@ class Api::V1::Statuses::ReblogsController < Api::BaseController end def destroy - @status = current_account.statuses.find_by(reblog_of_id: @reblog.id) + @status = current_account.statuses.find_by(reblog_of_id: params[:status_id]) if @status authorize @status, :unreblog? @status.discard RemovalWorker.perform_async(@status.id) + @reblog = @status.reblog + else + @reblog = Status.find(params[:status_id]) + authorize @reblog, :show? end render json: @reblog, serializer: REST::StatusSerializer, relationships: StatusRelationshipsPresenter.new([@status], current_account.id, reblogs_map: { @reblog.id => false }) + rescue Mastodon::NotPermittedError + not_found end private diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index c54f6643a..441833e85 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -48,6 +48,7 @@ class Auth::SessionsController < Devise::SessionsController user = User.authenticate_with_ldap(user_params) if Devise.ldap_authentication user ||= User.authenticate_with_pam(user_params) if Devise.pam_authentication user ||= User.find_for_authentication(email: user_params[:email]) + user end end diff --git a/app/controllers/tags_controller.rb b/app/controllers/tags_controller.rb index e46c0532c..69db89eb3 100644 --- a/app/controllers/tags_controller.rb +++ b/app/controllers/tags_controller.rb @@ -28,7 +28,7 @@ class TagsController < ApplicationController expires_in 0, public: true limit = params[:limit].present? ? [params[:limit].to_i, PAGE_SIZE_MAX].min : PAGE_SIZE - @statuses = HashtagQueryService.new.call(@tag, filter_params, nil, @local).limit(PAGE_SIZE) + @statuses = HashtagQueryService.new.call(@tag, filter_params, nil, @local).limit(limit) @statuses = cache_collection(@statuses, Status) render xml: RSS::TagSerializer.render(@tag, @statuses) diff --git a/app/javascript/mastodon/features/account_gallery/components/media_item.js b/app/javascript/mastodon/features/account_gallery/components/media_item.js index 9eb4ed0d3..c9a7af7f7 100644 --- a/app/javascript/mastodon/features/account_gallery/components/media_item.js +++ b/app/javascript/mastodon/features/account_gallery/components/media_item.js @@ -61,57 +61,9 @@ export default class MediaItem extends ImmutablePureComponent { const width = `${Math.floor((displayWidth - 4) / 3) - 4}px`; const height = width; const status = attachment.get('status'); - const title = status.get('spoiler_text') || attachment.get('description'); + const title = status.get('spoiler_text') || attachment.get('description'); - let thumbnail = ''; - let icon; - - if (attachment.get('type') === 'unknown') { - // Skip - } else if (attachment.get('type') === 'audio') { - thumbnail = ( - - - - ); - } else if (attachment.get('type') === 'image') { - const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0; - const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0; - const x = ((focusX / 2) + .5) * 100; - const y = ((focusY / -2) + .5) * 100; - - thumbnail = ( - {attachment.get('description')} - ); - } else if (['gifv', 'video'].indexOf(attachment.get('type')) !== -1) { - const autoPlay = !isIOS() && autoPlayGif; - const label = attachment.get('type') === 'video' ? : 'GIF'; - - thumbnail = ( -
-
- ); - } + let thumbnail, label, icon, content; if (!visible) { icon = ( @@ -119,6 +71,60 @@ export default class MediaItem extends ImmutablePureComponent { ); + } else { + if (['audio', 'video'].includes(attachment.get('type'))) { + content = ( + {attachment.get('description')} + ); + + if (attachment.get('type') === 'audio') { + label = ; + } else { + label = ; + } + } else if (attachment.get('type') === 'image') { + const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0; + const focusY = attachment.getIn(['meta', 'focus', 'y']) || 0; + const x = ((focusX / 2) + .5) * 100; + const y = ((focusY / -2) + .5) * 100; + + content = ( + {attachment.get('description')} + ); + } else if (attachment.get('type') === 'gifv') { + content = ( +