diff --git a/.github/workflows/build-security.yml b/.github/workflows/build-security.yml index b03b787d5..1e2455d3d 100644 --- a/.github/workflows/build-security.yml +++ b/.github/workflows/build-security.yml @@ -38,7 +38,7 @@ jobs: tags: | type=raw,value=edge type=raw,value=nightly - type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }} + type=raw,value=${{ needs.compute-suffix.outputs.prerelease }} secrets: inherit build-image-streaming: @@ -60,5 +60,5 @@ jobs: tags: | type=raw,value=edge type=raw,value=nightly - type=schedule,pattern=${{ needs.compute-suffix.outputs.prerelease }} + type=raw,value=${{ needs.compute-suffix.outputs.prerelease }} secrets: inherit diff --git a/.github/workflows/crowdin-download.yml b/.github/workflows/crowdin-download.yml index d3988d2f1..a676ff12f 100644 --- a/.github/workflows/crowdin-download.yml +++ b/.github/workflows/crowdin-download.yml @@ -52,7 +52,7 @@ jobs: # Create or update the pull request - name: Create Pull Request - uses: peter-evans/create-pull-request@v5.0.2 + uses: peter-evans/create-pull-request@v6.0.0 with: commit-message: 'New Crowdin translations' title: 'New Crowdin Translations (automated)' diff --git a/.github/workflows/test-ruby.yml b/.github/workflows/test-ruby.yml index 4275f5942..7fd259ae0 100644 --- a/.github/workflows/test-ruby.yml +++ b/.github/workflows/test-ruby.yml @@ -224,7 +224,7 @@ jobs: if: failure() with: name: e2e-screenshots - path: tmp/screenshots/ + path: tmp/capybara/ test-search: name: Elastic Search integration testing @@ -328,4 +328,4 @@ jobs: if: failure() with: name: test-search-screenshots - path: tmp/screenshots/ + path: tmp/capybara/ diff --git a/.rubocop.yml b/.rubocop.yml index 330c40de1..a8310489e 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -134,6 +134,11 @@ Rails/UnusedIgnoredColumns: Rails/NegateInclude: Enabled: false +# Reason: Enforce default limit, but allow some elements to span lines +# https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecexamplelength +RSpec/ExampleLength: + CountAsOne: ['array', 'heredoc', 'method_call'] + # Reason: Deprecated cop, will be removed in 3.0, replaced by SpecFilePathFormat # https://docs.rubocop.org/rubocop-rspec/cops_rspec.html#rspecfilepath RSpec/FilePath: diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml index 09e9fd73d..11ac57083 100644 --- a/.rubocop_todo.yml +++ b/.rubocop_todo.yml @@ -36,7 +36,7 @@ Metrics/PerceivedComplexity: # Configuration parameters: CountAsOne. RSpec/ExampleLength: - Max: 22 + Max: 20 # Override default of 5 RSpec/MultipleExpectations: Max: 7 diff --git a/CHANGELOG.md b/CHANGELOG.md index 6f775fcfa..a53790afa 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,101 @@ All notable changes to this project will be documented in this file. +## [4.2.7] - 2024-02-16 + +### Fixed + +- Fix OmniAuth tests and edge cases in error handling ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/29201), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/29207)) +- Fix new installs by upgrading to the latest release of the `nsa` gem, instead of a no longer existing commit ([mjankowski](https://github.com/mastodon/mastodon/pull/29065)) + +### Security + +- Fix insufficient checking of remote posts ([GHSA-jhrq-qvrm-qr36](https://github.com/mastodon/mastodon/security/advisories/GHSA-jhrq-qvrm-qr36)) + +## [4.2.6] - 2024-02-14 + +### Security + +- Update the `sidekiq-unique-jobs` dependency (see [GHSA-cmh9-rx85-xj38](https://github.com/mhenrixon/sidekiq-unique-jobs/security/advisories/GHSA-cmh9-rx85-xj38)) + In addition, we have disabled the web interface for `sidekiq-unique-jobs` out of caution. + If you need it, you can re-enable it by setting `ENABLE_SIDEKIQ_UNIQUE_JOBS_UI=true`. + If you only need to clear all locks, you can now use `bundle exec rake sidekiq_unique_jobs:delete_all_locks`. +- Update the `nokogiri` dependency (see [GHSA-xc9x-jj77-9p9j](https://github.com/sparklemotion/nokogiri/security/advisories/GHSA-xc9x-jj77-9p9j)) +- Disable administrative Doorkeeper routes ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/29187)) +- Fix ongoing streaming sessions not being invalidated when applications get deleted in some cases ([GHSA-7w3c-p9j8-mq3x](https://github.com/mastodon/mastodon/security/advisories/GHSA-7w3c-p9j8-mq3x)) + In some rare cases, the streaming server was not notified of access tokens revocation on application deletion. +- Change external authentication behavior to never reattach a new identity to an existing user by default ([GHSA-vm39-j3vx-pch3](https://github.com/mastodon/mastodon/security/advisories/GHSA-vm39-j3vx-pch3)) + Up until now, Mastodon has allowed new identities from external authentication providers to attach to an existing local user based on their verified e-mail address. + This allowed upgrading users from a database-stored password to an external authentication provider, or move from one authentication provider to another. + However, this behavior may be unexpected, and means that when multiple authentication providers are configured, the overall security would be that of the least secure authentication provider. + For these reasons, this behavior is now locked under the `ALLOW_UNSAFE_AUTH_PROVIDER_REATTACH` environment variable. + In addition, regardless of this environment variable, Mastodon will refuse to attach two identities from the same authentication provider to the same account. + +## [4.2.5] - 2024-02-01 + +### Security + +- Fix insufficient origin validation (CVE-2024-23832, [GHSA-3fjr-858r-92rw](https://github.com/mastodon/mastodon/security/advisories/GHSA-3fjr-858r-92rw)) + +## [4.2.4] - 2024-01-24 + +### Fixed + +- Fix error when processing remote files with unusually long names ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28823)) +- Fix processing of compacted single-item JSON-LD collections ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28816)) +- Retry 401 errors on replies fetching ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/28788)) +- Fix `RecordNotUnique` errors in LinkCrawlWorker ([tribela](https://github.com/mastodon/mastodon/pull/28748)) +- Fix Mastodon not correctly processing HTTP Signatures with query strings ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28443), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28476)) +- Fix potential redirection loop of streaming endpoint ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28665)) +- Fix streaming API redirection ignoring the port of `streaming_api_base_url` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28558)) +- Fix error when processing link preview with an array as `inLanguage` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28252)) +- Fix unsupported time zone or locale preventing sign-up ([Gargron](https://github.com/mastodon/mastodon/pull/28035)) +- Fix "Hide these posts from home" list setting not refreshing when switching lists ([brianholley](https://github.com/mastodon/mastodon/pull/27763)) +- Fix missing background behind dismissable banner in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/27479)) +- Fix line wrapping of language selection button with long locale codes ([gunchleoc](https://github.com/mastodon/mastodon/pull/27100), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27127)) +- Fix `Undo Announce` activity not being sent to non-follower authors ([MitarashiDango](https://github.com/mastodon/mastodon/pull/18482)) +- Fix N+1s because of association preloaders not actually getting called ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28339)) +- Fix empty column explainer getting cropped under certain conditions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28337)) +- Fix `LinkCrawlWorker` error when encountering empty OEmbed response ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28268)) +- Fix call to inefficient `delete_matched` cache method in domain blocks ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28367)) + +### Security + +- Add rate-limit of TOTP authentication attempts at controller level ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/28801)) + +## [4.2.3] - 2023-12-05 + +### Fixed + +- Fix dependency on `json-canonicalization` version that has been made unavailable since last release + +## [4.2.2] - 2023-12-04 + +### Changed + +- Change dismissed banners to be stored server-side ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27055)) +- Change GIF max matrix size error to explicitly mention GIF files ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27927)) +- Change `Follow` activities delivery to bypass availability check ([ShadowJonathan](https://github.com/mastodon/mastodon/pull/27586)) +- Change single-column navigation notice to be displayed outside of the logo container ([renchap](https://github.com/mastodon/mastodon/pull/27462), [renchap](https://github.com/mastodon/mastodon/pull/27476)) +- Change Content-Security-Policy to be tighter on media paths ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26889)) +- Change post language code to include country code when relevant ([gunchleoc](https://github.com/mastodon/mastodon/pull/27099), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27207)) + +### Fixed + +- Fix upper border radius of onboarding columns ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27890)) +- Fix incoming status creation date not being restricted to standard ISO8601 ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27655), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/28081)) +- Fix some posts from threads received out-of-order sometimes not being inserted into timelines ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27653)) +- Fix posts from force-sensitized accounts being able to trend ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27620)) +- Fix error when trying to delete already-deleted file with OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27569)) +- Fix batch attachment deletion when using OpenStack Swift ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27554)) +- Fix processing LDSigned activities from actors with unknown public keys ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27474)) +- Fix error and incorrect URLs in `/api/v1/accounts/:id/featured_tags` for remote accounts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27459)) +- Fix report processing notice not mentioning the report number when performing a custom action ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27442)) +- Fix handling of `inLanguage` attribute in preview card processing ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27423)) +- Fix own posts being removed from home timeline when unfollowing a used hashtag ([kmycode](https://github.com/mastodon/mastodon/pull/27391)) +- Fix some link anchors being recognized as hashtags ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27271), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/27584)) +- Fix format-dependent redirects being cached regardless of requested format ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27634)) + ## [4.2.1] - 2023-10-10 ### Added diff --git a/Gemfile.lock b/Gemfile.lock index 57ad96437..0b53df82e 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -146,7 +146,7 @@ GEM blurhash (0.1.7) bootsnap (1.18.3) msgpack (~> 1.2) - brakeman (6.1.1) + brakeman (6.1.2) racc browser (5.3.1) brpoplpush-redis_script (0.1.3) @@ -182,7 +182,8 @@ GEM cose (1.3.0) cbor (~> 0.5.9) openssl-signature_algorithm (~> 1.0) - crack (0.4.5) + crack (0.4.6) + bigdecimal rexml crass (1.0.6) css_parser (1.14.0) @@ -314,7 +315,7 @@ GEM rainbow rubocop (>= 1.0) sysexits (~> 1.1) - hashdiff (1.0.1) + hashdiff (1.1.0) hashie (5.0.0) hcaptcha (7.1.0) json @@ -350,7 +351,7 @@ GEM terminal-table (>= 1.5.1) idn-ruby (0.1.5) io-console (0.7.2) - irb (1.11.1) + irb (1.11.2) rdoc reline (>= 0.4.2) jmespath (1.6.2) @@ -504,8 +505,8 @@ GEM parslet (2.0.0) pastel (0.8.0) tty-color (~> 0.5) - pg (1.5.4) - pghero (3.4.0) + pg (1.5.5) + pghero (3.4.1) activerecord (>= 6) posix-spawn (0.3.15) premailer (1.21.0) @@ -706,7 +707,7 @@ GEM rufus-scheduler (~> 3.2) sidekiq (>= 6, < 8) tilt (>= 1.4.0) - sidekiq-unique-jobs (7.1.31) + sidekiq-unique-jobs (7.1.33) brpoplpush-redis_script (> 0.1.1, <= 2.0.0) concurrent-ruby (~> 1.0, >= 1.0.5) redis (< 5.0) @@ -792,7 +793,7 @@ GEM webfinger (1.2.0) activesupport httpclient (>= 2.4) - webmock (3.19.1) + webmock (3.20.0) addressable (>= 2.8.0) crack (>= 0.3.2) hashdiff (>= 0.4.0, < 2.0.0) diff --git a/README.md b/README.md index 267f0ed29..6cf722b35 100644 --- a/README.md +++ b/README.md @@ -69,7 +69,7 @@ Mastodon acts as an OAuth2 provider, so 3rd party apps can use the REST and Stre - **PostgreSQL** 12+ - **Redis** 4+ -- **Ruby** 2.7+ +- **Ruby** 3.0+ - **Node.js** 16+ The repository includes deployment configurations for **Docker and docker-compose** as well as specific platforms like **Heroku**, **Scalingo**, and **Nanobox**. For Helm charts, reference the [mastodon/chart repository](https://github.com/mastodon/chart). The [**standalone** installation guide](https://docs.joinmastodon.org/admin/install/) is available in the documentation. diff --git a/app/controllers/activitypub/inboxes_controller.rb b/app/controllers/activitypub/inboxes_controller.rb index ba85e0a72..e8b0f47cd 100644 --- a/app/controllers/activitypub/inboxes_controller.rb +++ b/app/controllers/activitypub/inboxes_controller.rb @@ -62,11 +62,10 @@ class ActivityPub::InboxesController < ActivityPub::BaseController return if raw_params.blank? || ENV['DISABLE_FOLLOWERS_SYNCHRONIZATION'] == 'true' || signed_request_account.nil? # Re-using the syntax for signature parameters - tree = SignatureParamsParser.new.parse(raw_params) - params = SignatureParamsTransformer.new.apply(tree) + params = SignatureParser.parse(raw_params) ActivityPub::PrepareFollowersSynchronizationService.new.call(signed_request_account, params) - rescue Parslet::ParseFailed + rescue SignatureParser::ParsingError Rails.logger.warn 'Error parsing Collection-Synchronization header' end diff --git a/app/controllers/api/v1/statuses_controller.rb b/app/controllers/api/v1/statuses_controller.rb index 064e7632a..01c371876 100644 --- a/app/controllers/api/v1/statuses_controller.rb +++ b/app/controllers/api/v1/statuses_controller.rb @@ -71,13 +71,9 @@ class Api::V1::StatusesController < Api::BaseController with_rate_limit: true ) - render json: @status, serializer: @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer + render json: @status, serializer: serializer_for_status rescue PostStatusService::UnexpectedMentionsError => e - unexpected_accounts = ActiveModel::Serializer::CollectionSerializer.new( - e.accounts, - serializer: REST::AccountSerializer - ) - render json: { error: e.message, unexpected_accounts: unexpected_accounts }, status: 422 + render json: unexpected_accounts_error_json(e), status: 422 end def update @@ -155,6 +151,21 @@ class Api::V1::StatusesController < Api::BaseController ) end + def serializer_for_status + @status.is_a?(ScheduledStatus) ? REST::ScheduledStatusSerializer : REST::StatusSerializer + end + + def unexpected_accounts_error_json(error) + { + error: error.message, + unexpected_accounts: serialized_accounts(error.accounts), + } + end + + def serialized_accounts(accounts) + ActiveModel::Serializer::CollectionSerializer.new(accounts, serializer: REST::AccountSerializer) + end + def pagination_params(core_params) params.slice(:limit).permit(:limit).merge(core_params) end diff --git a/app/controllers/auth/omniauth_callbacks_controller.rb b/app/controllers/auth/omniauth_callbacks_controller.rb index 707b50ef9..9d496220a 100644 --- a/app/controllers/auth/omniauth_callbacks_controller.rb +++ b/app/controllers/auth/omniauth_callbacks_controller.rb @@ -7,7 +7,7 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController def self.provides_callback_for(provider) define_method provider do @provider = provider - @user = User.find_for_oauth(request.env['omniauth.auth'], current_user) + @user = User.find_for_omniauth(request.env['omniauth.auth'], current_user) if @user.persisted? record_login_activity @@ -17,6 +17,9 @@ class Auth::OmniauthCallbacksController < Devise::OmniauthCallbacksController session["devise.#{provider}_data"] = request.env['omniauth.auth'] redirect_to new_user_registration_url end + rescue ActiveRecord::RecordInvalid + flash[:alert] = I18n.t('devise.failure.omniauth_user_creation_failure') if is_navigational_format? + redirect_to new_user_session_url end end diff --git a/app/controllers/auth/sessions_controller.rb b/app/controllers/auth/sessions_controller.rb index 962b78de6..6ed7b2baa 100644 --- a/app/controllers/auth/sessions_controller.rb +++ b/app/controllers/auth/sessions_controller.rb @@ -183,7 +183,9 @@ class Auth::SessionsController < Devise::SessionsController ) # Only send a notification email every hour at most - return if redis.set("2fa_failure_notification:#{user.id}", '1', ex: 1.hour, get: true).present? + return if redis.get("2fa_failure_notification:#{user.id}").present? + + redis.set("2fa_failure_notification:#{user.id}", '1', ex: 1.hour) UserMailer.failed_2fa(user, request.remote_ip, request.user_agent, Time.now.utc).deliver_later! end diff --git a/app/controllers/concerns/signature_verification.rb b/app/controllers/concerns/signature_verification.rb index 92f1eb5a1..315586627 100644 --- a/app/controllers/concerns/signature_verification.rb +++ b/app/controllers/concerns/signature_verification.rb @@ -12,39 +12,6 @@ module SignatureVerification class SignatureVerificationError < StandardError; end - class SignatureParamsParser < Parslet::Parser - rule(:token) { match("[0-9a-zA-Z!#$%&'*+.^_`|~-]").repeat(1).as(:token) } - rule(:quoted_string) { str('"') >> (qdtext | quoted_pair).repeat.as(:quoted_string) >> str('"') } - # qdtext and quoted_pair are not exactly according to spec but meh - rule(:qdtext) { match('[^\\\\"]') } - rule(:quoted_pair) { str('\\') >> any } - rule(:bws) { match('\s').repeat } - rule(:param) { (token.as(:key) >> bws >> str('=') >> bws >> (token | quoted_string).as(:value)).as(:param) } - rule(:comma) { bws >> str(',') >> bws } - # Old versions of node-http-signature add an incorrect "Signature " prefix to the header - rule(:buggy_prefix) { str('Signature ') } - rule(:params) { buggy_prefix.maybe >> (param >> (comma >> param).repeat).as(:params) } - root(:params) - end - - class SignatureParamsTransformer < Parslet::Transform - rule(params: subtree(:param)) do - (param.is_a?(Array) ? param : [param]).each_with_object({}) { |(key, value), hash| hash[key] = value } - end - - rule(param: { key: simple(:key), value: simple(:val) }) do - [key, val] - end - - rule(quoted_string: simple(:string)) do - string.to_s - end - - rule(token: simple(:string)) do - string.to_s - end - end - def require_account_signature! render json: signature_verification_failure_reason, status: signature_verification_failure_code unless signed_request_account end @@ -135,12 +102,8 @@ module SignatureVerification end def signature_params - @signature_params ||= begin - raw_signature = request.headers['Signature'] - tree = SignatureParamsParser.new.parse(raw_signature) - SignatureParamsTransformer.new.apply(tree) - end - rescue Parslet::ParseFailed + @signature_params ||= SignatureParser.parse(request.headers['Signature']) + rescue SignatureParser::ParsingError raise SignatureVerificationError, 'Error parsing signature parameters' end diff --git a/app/helpers/jsonld_helper.rb b/app/helpers/jsonld_helper.rb index cc05b7a40..b0f2077db 100644 --- a/app/helpers/jsonld_helper.rb +++ b/app/helpers/jsonld_helper.rb @@ -174,7 +174,19 @@ module JsonLdHelper build_request(uri, on_behalf_of, options: request_options).perform do |response| raise Mastodon::UnexpectedResponseError, response unless response_successful?(response) || response_error_unsalvageable?(response) || !raise_on_temporary_error - body_to_json(response.body_with_limit) if response.code == 200 + body_to_json(response.body_with_limit) if response.code == 200 && valid_activitypub_content_type?(response) + end + end + + def valid_activitypub_content_type?(response) + return true if response.mime_type == 'application/activity+json' + + # When the mime type is `application/ld+json`, we need to check the profile, + # but `http.rb` does not parse it for us. + return false unless response.mime_type == 'application/ld+json' + + response.headers[HTTP::Headers::CONTENT_TYPE]&.split(';')&.map(&:strip)&.any? do |str| + str.start_with?('profile="') && str[9...-1].split.include?('https://www.w3.org/ns/activitystreams') end end diff --git a/app/javascript/mastodon/features/compose/components/upload_button.jsx b/app/javascript/mastodon/features/compose/components/upload_button.jsx index 3866f239d..50c9ad632 100644 --- a/app/javascript/mastodon/features/compose/components/upload_button.jsx +++ b/app/javascript/mastodon/features/compose/components/upload_button.jsx @@ -65,6 +65,7 @@ class UploadButton extends ImmutablePureComponent { key={resetFileKey} ref={this.setRef} type='file' + name='file-upload-input' multiple accept={acceptContentTypes.toArray().join(',')} onChange={this.handleChange} diff --git a/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx b/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx index ac414d04d..f76526e04 100644 --- a/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx +++ b/app/javascript/mastodon/features/home_timeline/components/inline_follow_suggestions.jsx @@ -59,7 +59,7 @@ Source.propTypes = { id: PropTypes.oneOf(['friends_of_friends', 'similar_to_recently_followed', 'featured', 'most_followed', 'most_interactions']), }; -const Card = ({ id, source }) => { +const Card = ({ id, sources }) => { const intl = useIntl(); const account = useSelector(state => state.getIn(['accounts', id])); const relationship = useSelector(state => state.getIn(['relationships', id])); @@ -89,7 +89,7 @@ const Card = ({ id, source }) => {
- {firstVerifiedField ? : } + {firstVerifiedField ? : }