From 36b57037961383466b7f5c20b39ee68cd9f202a0 Mon Sep 17 00:00:00 2001 From: Rey Tucker Date: Tue, 20 Mar 2018 04:06:08 -0400 Subject: [PATCH 1/9] request: in the event of failure, try other IPs (#6761) (#6813) * request: in the event of failure, try other IPs (#6761) In the case where a name has multiple A/AAAA records, we should try subsequent records instead of immediately failing when we have a failure on the first IP address. This significantly improves delivery success when there are network connectivity problems affecting only IPv4 or IPv6. * fix method call style * request_spec: adjust test case to use Addrinfo * request: Request/open: move private addr check to within begin/rescue * request_spec: add case to test failover, fix exception check * Double Addrinfo.foreach so that it correctly yields instances --- app/lib/request.rb | 13 ++++++++++--- spec/lib/request_spec.rb | 11 ++++++++++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/app/lib/request.rb b/app/lib/request.rb index 5776b3d78..298fb9528 100644 --- a/app/lib/request.rb +++ b/app/lib/request.rb @@ -94,9 +94,16 @@ class Request class Socket < TCPSocket class << self def open(host, *args) - address = IPSocket.getaddress(host) - raise Mastodon::HostValidationError if PrivateAddressCheck.private_address? IPAddr.new(address) - super address, *args + outer_e = nil + Addrinfo.foreach(host, nil, nil, :SOCK_STREAM) do |address| + begin + raise Mastodon::HostValidationError if PrivateAddressCheck.private_address? IPAddr.new(address.ip_address) + return super address.ip_address, *args + rescue => e + outer_e = e + end + end + raise outer_e if outer_e end alias new open diff --git a/spec/lib/request_spec.rb b/spec/lib/request_spec.rb index dc7daa52c..5da357c55 100644 --- a/spec/lib/request_spec.rb +++ b/spec/lib/request_spec.rb @@ -48,6 +48,13 @@ describe Request do expect(a_request(:get, 'http://example.com')).to have_been_made.once end + it 'executes a HTTP request when the first address is private' do + allow(Addrinfo).to receive(:foreach).with('example.com', nil, nil, :SOCK_STREAM) + .and_yield(Addrinfo.new(["AF_INET", 0, "example.com", "0.0.0.0"], :PF_INET, :SOCK_STREAM)) + .and_yield(Addrinfo.new(["AF_INET6", 0, "example.com", "2001:4860:4860::8844"], :PF_INET6, :SOCK_STREAM)) + expect(a_request(:get, 'http://example.com')).to have_been_made.once + end + it 'sets headers' do expect(a_request(:get, 'http://example.com').with(headers: subject.headers)).to have_been_made end @@ -61,7 +68,9 @@ describe Request do end it 'raises Mastodon::ValidationError' do - allow(IPSocket).to receive(:getaddress).with('example.com').and_return('0.0.0.0') + allow(Addrinfo).to receive(:foreach).with('example.com', nil, nil, :SOCK_STREAM) + .and_yield(Addrinfo.new(["AF_INET", 0, "example.com", "0.0.0.0"], :PF_INET, :SOCK_STREAM)) + .and_yield(Addrinfo.new(["AF_INET6", 0, "example.com", "2001:db8::face"], :PF_INET6, :SOCK_STREAM)) expect{ subject.perform }.to raise_error Mastodon::ValidationError end end From a5c6c748e096f61d00bbd778a263e22117e1ae9f Mon Sep 17 00:00:00 2001 From: ThibG Date: Tue, 20 Mar 2018 12:40:12 +0100 Subject: [PATCH 2/9] Cancel outdated pending compose suggestions (#6838) --- app/javascript/mastodon/actions/compose.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/app/javascript/mastodon/actions/compose.js b/app/javascript/mastodon/actions/compose.js index 130b4af23..1371f22b2 100644 --- a/app/javascript/mastodon/actions/compose.js +++ b/app/javascript/mastodon/actions/compose.js @@ -1,4 +1,5 @@ import api from '../api'; +import { CancelToken } from 'axios'; import { throttle } from 'lodash'; import { search as emojiSearch } from '../features/emoji/emoji_mart_search_light'; import { tagHistory } from '../settings'; @@ -11,6 +12,8 @@ import { refreshPublicTimeline, } from './timelines'; +let cancelFetchComposeSuggestionsAccounts; + export const COMPOSE_CHANGE = 'COMPOSE_CHANGE'; export const COMPOSE_SUBMIT_REQUEST = 'COMPOSE_SUBMIT_REQUEST'; export const COMPOSE_SUBMIT_SUCCESS = 'COMPOSE_SUBMIT_SUCCESS'; @@ -257,13 +260,22 @@ export function undoUploadCompose(media_id) { }; export function clearComposeSuggestions() { + if (cancelFetchComposeSuggestionsAccounts) { + cancelFetchComposeSuggestionsAccounts(); + } return { type: COMPOSE_SUGGESTIONS_CLEAR, }; }; const fetchComposeSuggestionsAccounts = throttle((dispatch, getState, token) => { + if (cancelFetchComposeSuggestionsAccounts) { + cancelFetchComposeSuggestionsAccounts(); + } api(getState).get('/api/v1/accounts/search', { + cancelToken: new CancelToken(cancel => { + cancelFetchComposeSuggestionsAccounts = cancel; + }), params: { q: token.slice(1), resolve: false, From 9381a7d9d55ea734d6c498a82d17d73fd02fbe87 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 20 Mar 2018 14:57:46 +0100 Subject: [PATCH 3/9] Use username/domain to match existing accounts in ActivityPub (#6842) See also: #6837, #6667 --- app/services/activitypub/process_account_service.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 68e9db766..7d8dc1369 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -16,7 +16,7 @@ class ActivityPub::ProcessAccountService < BaseService RedisLock.acquire(lock_options) do |lock| if lock.acquired? - @account = Account.find_by(uri: @uri) + @account = Account.find_remote(@username, @domain) @old_public_key = @account&.public_key @old_protocol = @account&.protocol From 61dcb686a8f0a3272e2948c9a072aa58593a7409 Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 21 Mar 2018 00:36:20 +0900 Subject: [PATCH 4/9] Fix i18n fallback configuration conflicts with environment configurations (#6843) --- config/application.rb | 4 +--- config/environments/production.rb | 4 ++-- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/config/application.rb b/config/application.rb index 326a0ec8c..385bd4704 100644 --- a/config/application.rb +++ b/config/application.rb @@ -76,9 +76,7 @@ module Mastodon ] config.i18n.default_locale = ENV['DEFAULT_LOCALE']&.to_sym - if config.i18n.available_locales.include?(config.i18n.default_locale) - config.i18n.fallbacks = [:en] - else + unless config.i18n.available_locales.include?(config.i18n.default_locale) config.i18n.default_locale = :en end diff --git a/config/environments/production.rb b/config/environments/production.rb index 3136a40fc..f372cd363 100644 --- a/config/environments/production.rb +++ b/config/environments/production.rb @@ -55,8 +55,8 @@ Rails.application.configure do # config.action_mailer.raise_delivery_errors = false # Enable locale fallbacks for I18n (makes lookups for any locale fall back to - # the I18n.default_locale when a translation cannot be found). - config.i18n.fallbacks = true + # English when a translation cannot be found). + config.i18n.fallbacks = [:en] # Send deprecation notices to registered listeners. config.active_support.deprecation = :notify From ac49c7932d848fbb946c37a69f42b7dbc774c56c Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 20 Mar 2018 19:41:51 +0100 Subject: [PATCH 5/9] Add LDAP_TLS_NO_VERIFY option, don't require LDAP_ENABLED outside .env (#6845) Fix #6816, fix #6790 --- config/initializers/devise.rb | 3 ++ lib/devise/ldap_authenticatable.rb | 76 ++++++++++++++++-------------- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 97757d0fb..e0d263f16 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -55,6 +55,8 @@ module Devise @@ldap_bind_dn = nil mattr_accessor :ldap_password @@ldap_password = nil + mattr_accessor :ldap_tls_no_verify + @@ldap_tls_no_verify = false class Strategies::PamAuthenticatable def valid? @@ -357,5 +359,6 @@ Devise.setup do |config| config.ldap_bind_dn = ENV.fetch('LDAP_BIND_DN') config.ldap_password = ENV.fetch('LDAP_PASSWORD') config.ldap_uid = ENV.fetch('LDAP_UID', 'cn') + config.ldap_tls_no_verify = ENV['LDAP_TLS_NO_VERIFY'] == 'true' end end diff --git a/lib/devise/ldap_authenticatable.rb b/lib/devise/ldap_authenticatable.rb index 531abdbbe..ef786fbb7 100644 --- a/lib/devise/ldap_authenticatable.rb +++ b/lib/devise/ldap_authenticatable.rb @@ -1,49 +1,53 @@ # frozen_string_literal: true -if ENV['LDAP_ENABLED'] == 'true' - require 'net/ldap' - require 'devise/strategies/authenticatable' +require 'net/ldap' +require 'devise/strategies/authenticatable' - module Devise - module Strategies - class LdapAuthenticatable < Authenticatable - def authenticate! - if params[:user] - ldap = Net::LDAP.new( - host: Devise.ldap_host, - port: Devise.ldap_port, - base: Devise.ldap_base, - encryption: { - method: Devise.ldap_method, - tls_options: OpenSSL::SSL::SSLContext::DEFAULT_PARAMS, - }, - auth: { - method: :simple, - username: Devise.ldap_bind_dn, - password: Devise.ldap_password, - }, - connect_timeout: 10 - ) +module Devise + module Strategies + class LdapAuthenticatable < Authenticatable + def authenticate! + if params[:user] + ldap = Net::LDAP.new( + host: Devise.ldap_host, + port: Devise.ldap_port, + base: Devise.ldap_base, + encryption: { + method: Devise.ldap_method, + tls_options: tls_options, + }, + auth: { + method: :simple, + username: Devise.ldap_bind_dn, + password: Devise.ldap_password, + }, + connect_timeout: 10 + ) - if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: "(#{Devise.ldap_uid}=#{email})", password: password)) - user = User.ldap_get_user(user_info.first) - success!(user) - else - return fail(:invalid_login) - end + if (user_info = ldap.bind_as(base: Devise.ldap_base, filter: "(#{Devise.ldap_uid}=#{email})", password: password)) + user = User.ldap_get_user(user_info.first) + success!(user) + else + return fail(:invalid_login) end end + end - def email - params[:user][:email] - end + def email + params[:user][:email] + end - def password - params[:user][:password] + def password + params[:user][:password] + end + + def tls_options + OpenSSL::SSL::SSLContext::DEFAULT_PARAMS.tap do |options| + options[:verify_mode] = OpenSSL::SSL::VERIFY_NONE if Devise.ldap_tls_no_verify end end end end - - Warden::Strategies.add(:ldap_authenticatable, Devise::Strategies::LdapAuthenticatable) end + +Warden::Strategies.add(:ldap_authenticatable, Devise::Strategies::LdapAuthenticatable) From f64af6473fd1c61190ede960791efddc29806f92 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Tue, 20 Mar 2018 23:49:24 +0100 Subject: [PATCH 6/9] Bump version to 2.3.2rc4 --- lib/mastodon/version.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 78a2dd901..80650761a 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -21,7 +21,7 @@ module Mastodon end def flags - 'rc3' + 'rc4' end def to_a From a6b59cd1a32ce2d9ac54fa7b5e04672a63692fdf Mon Sep 17 00:00:00 2001 From: Akihiko Odaki Date: Wed, 21 Mar 2018 18:26:15 +0900 Subject: [PATCH 7/9] Remove debug option from Babel preset env (#6852) --- .babelrc | 1 - 1 file changed, 1 deletion(-) diff --git a/.babelrc b/.babelrc index ed28aa500..190b5038c 100644 --- a/.babelrc +++ b/.babelrc @@ -4,7 +4,6 @@ [ "env", { - "debug": true, "exclude": ["transform-async-to-generator", "transform-regenerator"], "loose": true, "modules": false, From 93897134caf42f1b70620282cef04865af7026b1 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Wed, 21 Mar 2018 10:26:53 +0100 Subject: [PATCH 8/9] Permit dots in usernames with conditions (#6844) * Permit dots in usernames with conditions - Dot cannot be the start or end of username - a.lice and al.ice are considered the same during sign-up * Fix regex mixin flags --- app/models/account.rb | 6 ++++-- app/validators/unique_username_validator.rb | 14 ++++++++++++++ 2 files changed, 18 insertions(+), 2 deletions(-) create mode 100644 app/validators/unique_username_validator.rb diff --git a/app/models/account.rb b/app/models/account.rb index c1347fe65..14269860f 100644 --- a/app/models/account.rb +++ b/app/models/account.rb @@ -47,7 +47,8 @@ # class Account < ApplicationRecord - MENTION_RE = /(?<=^|[^\/[:word:]])@(([a-z0-9_]+)(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i + USERNAME_RE = /[a-z0-9_]+([a-z0-9_\.]+[a-z0-9_]+)?/i + MENTION_RE = /(?<=^|[^\/[:word:]])@((#{USERNAME_RE}?)(?:@[a-z0-9\.\-]+[a-z0-9]+)?)/i include AccountAvatar include AccountFinderConcern @@ -68,7 +69,8 @@ class Account < ApplicationRecord validates :username, uniqueness: { scope: :domain, case_sensitive: true }, if: -> { !local? && will_save_change_to_username? } # Local user validations - validates :username, format: { with: /\A[a-z0-9_]+\z/i }, uniqueness: { scope: :domain, case_sensitive: false }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? } + validates :username, format: { with: /\A#{USERNAME_RE}\z/i }, length: { maximum: 30 }, if: -> { local? && will_save_change_to_username? } + validates_with UniqueUsernameValidator, if: -> { local? && will_save_change_to_username? } validates_with UnreservedUsernameValidator, if: -> { local? && will_save_change_to_username? } validates :display_name, length: { maximum: 30 }, if: -> { local? && will_save_change_to_display_name? } validates :note, length: { maximum: 160 }, if: -> { local? && will_save_change_to_note? } diff --git a/app/validators/unique_username_validator.rb b/app/validators/unique_username_validator.rb new file mode 100644 index 000000000..c76407b16 --- /dev/null +++ b/app/validators/unique_username_validator.rb @@ -0,0 +1,14 @@ +# frozen_string_literal: true + +class UniqueUsernameValidator < ActiveModel::Validator + def validate(account) + return if account.username.nil? + + normalized_username = account.username.downcase.delete('.') + + scope = Account.where(domain: nil, username: normalized_username) + scope = scope.where.not(id: account.id) if account.persisted? + + account.errors.add(:username, :taken) if scope.exists? + end +end From d97903a3587e137316adbd8a9f0460552b5bfbcd Mon Sep 17 00:00:00 2001 From: Patrick Figel Date: Wed, 21 Mar 2018 17:43:28 +0100 Subject: [PATCH 9/9] Update sanitize and loofah (#6855) Fixes CVE-2018-8048 and CVE-2018-3740, two medium-severity XSS vulnerabilities present in these gems when built against libxml2 >= 2.9.2. --- Gemfile | 2 +- Gemfile.lock | 12 ++++++------ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Gemfile b/Gemfile index fe5bf572c..8bc28b893 100644 --- a/Gemfile +++ b/Gemfile @@ -71,7 +71,7 @@ gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock' gem 'rqrcode', '~> 0.10' gem 'ruby-oembed', '~> 0.12', require: 'oembed' gem 'ruby-progressbar', '~> 1.4' -gem 'sanitize', '~> 4.4' +gem 'sanitize', '~> 4.6.4' gem 'sidekiq', '~> 5.0' gem 'sidekiq-scheduler', '~> 2.1' gem 'sidekiq-unique-jobs', '~> 5.0' diff --git a/Gemfile.lock b/Gemfile.lock index ca6365c74..7360ce7f6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -288,7 +288,7 @@ GEM activesupport (>= 4, < 5.2) railties (>= 4, < 5.2) request_store (~> 1.0) - loofah (2.1.1) + loofah (2.2.1) crass (~> 1.0.2) nokogiri (>= 1.5.9) mail (2.7.0) @@ -316,9 +316,9 @@ GEM net-ssh (>= 2.6.5) net-ssh (4.2.0) nio4r (2.1.0) - nokogiri (1.8.1) + nokogiri (1.8.2) mini_portile2 (~> 2.3.0) - nokogumbo (1.4.13) + nokogumbo (1.5.0) nokogiri nsa (0.2.4) activesupport (>= 4.2, < 6) @@ -496,10 +496,10 @@ GEM rufus-scheduler (3.4.2) et-orbi (~> 1.0) safe_yaml (1.0.4) - sanitize (4.5.0) + sanitize (4.6.4) crass (~> 1.0.2) nokogiri (>= 1.4.4) - nokogumbo (~> 1.4.1) + nokogumbo (~> 1.4) sass (3.5.3) sass-listen (~> 4.0.0) sass-listen (4.0.0) @@ -699,7 +699,7 @@ DEPENDENCIES rubocop ruby-oembed (~> 0.12) ruby-progressbar (~> 1.4) - sanitize (~> 4.4) + sanitize (~> 4.6.4) scss_lint (~> 0.55) sidekiq (~> 5.0) sidekiq-bulk (~> 0.1.1)