diff --git a/Gemfile.lock b/Gemfile.lock index fc2beaa711124b..c52f908a58ec03 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -99,7 +99,7 @@ GEM ast (2.4.3) attr_required (1.0.2) aws-eventstream (1.4.0) - aws-partitions (1.1227.0) + aws-partitions (1.1238.0) aws-sdk-core (3.244.0) aws-eventstream (~> 1, >= 1.3.0) aws-partitions (~> 1, >= 1.992.0) @@ -111,7 +111,7 @@ GEM aws-sdk-kms (1.123.0) aws-sdk-core (~> 3, >= 3.244.0) aws-sigv4 (~> 1.5) - aws-sdk-s3 (1.217.0) + aws-sdk-s3 (1.219.0) aws-sdk-core (~> 3, >= 3.244.0) aws-sdk-kms (~> 1) aws-sigv4 (~> 1.5) @@ -121,7 +121,7 @@ GEM rexml base64 (0.3.0) bcp47_spec (0.2.1) - bcrypt (3.1.21) + bcrypt (3.1.22) benchmark (0.5.0) better_errors (2.10.1) erubi (>= 1.0.0) @@ -631,7 +631,7 @@ GEM activesupport (>= 3.0.0) raabro (1.4.0) racc (1.8.1) - rack (3.2.5) + rack (3.2.6) rack-attack (6.8.0) rack (>= 1.0, < 4) rack-cors (3.0.0) @@ -650,7 +650,7 @@ GEM rack (>= 3.0.0, < 4) rack-proxy (0.7.7) rack - rack-session (2.1.1) + rack-session (2.1.2) base64 (>= 0.1.0) rack (>= 3.0.0) rack-test (2.2.0) diff --git a/app/helpers/context_helper.rb b/app/helpers/context_helper.rb index 4f0327e3704bc0..b0db74dfeac425 100644 --- a/app/helpers/context_helper.rb +++ b/app/helpers/context_helper.rb @@ -44,7 +44,7 @@ module ContextHelper }, quote_requests: { 'QuoteRequest' => 'https://w3id.org/fep/044f#QuoteRequest' }, quotes: { - 'quote' => 'https://w3id.org/fep/044f#quote', + 'quote' => { '@id' => 'https://w3id.org/fep/044f#quote', '@type' => '@id' }, 'quoteUri' => 'http://fedibird.com/ns#quoteUri', '_misskey_quote' => 'https://misskey-hub.net/ns#_misskey_quote', 'quoteAuthorization' => { '@id' => 'https://w3id.org/fep/044f#quoteAuthorization', '@type' => '@id' }, diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index 802d55c43cf265..78844594426683 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -47,8 +47,10 @@ export function updateEmojiReactions(emoji_reaction) { export function updateNotifications(notification, intlMessages, intlLocale) { return (dispatch, getState) => { - const showAlert = getState().getIn(['settings', 'notifications', 'alerts', notification.type], true); - const playSound = getState().getIn(['settings', 'notifications', 'sounds', notification.type], true); + const filterType = notification.type === 'quoted_update' ? 'update' : notification.type; + + const showAlert = getState().getIn(['settings', 'notifications', 'alerts', filterType], true); + const playSound = getState().getIn(['settings', 'notifications', 'sounds', filterType], true); let filtered = false; diff --git a/app/models/user.rb b/app/models/user.rb index 718087e32937ff..07980e0ba7e70a 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -97,7 +97,7 @@ class User < ApplicationRecord has_one :custom_css, inverse_of: :user, dependent: :destroy - validates :email, presence: true, email_address: true + validates :email, presence: true, email_address: true, length: { maximum: 320 } validates_with UserEmailValidator, if: -> { ENV['EMAIL_DOMAIN_LISTS_APPLY_AFTER_CONFIRMATION'] == 'true' || !confirmed? } validates_with EmailMxValidator, if: :validate_email_dns? diff --git a/app/policies/status_policy.rb b/app/policies/status_policy.rb index 0ff63f044c71ca..a3a3ad2ae994af 100644 --- a/app/policies/status_policy.rb +++ b/app/policies/status_policy.rb @@ -26,9 +26,8 @@ def show_activity? following_author_domain? end - # This is about requesting a quote post, not validating it def quote? - show? && record.quote_policy_for_account(current_account) != :denied + show? && !blocking_author? && record.quote_policy_for_account(current_account) != :denied end def reblog? diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 35a380c9cfb4fa..f84467eb40b502 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -32,8 +32,15 @@ def call(username, domain, json, options = {}) @options[:request_id] ||= "#{Time.now.utc.to_i}-#{username}@#{domain}" with_redis_lock("process_account:#{@uri}") do - @account = Account.remote.find_by(uri: @uri) if @options[:only_key] - @account ||= Account.find_remote(@username, @domain) + if @options[:only_key] + # `only_key` is used to update an existing account known by its `uri`. + # Lookup by handle and new account creation do not make sense in this case. + @account = Account.remote.find_by(uri: @uri) + return if @account.nil? + else + @account = Account.find_remote(@username, @domain) + end + @old_public_key = @account&.public_key @old_protocol = @account&.protocol @old_searchability = @account&.searchability diff --git a/app/validators/email_address_validator.rb b/app/validators/email_address_validator.rb index ed0bb116524aec..7cc303a6369ead 100644 --- a/app/validators/email_address_validator.rb +++ b/app/validators/email_address_validator.rb @@ -11,8 +11,14 @@ def validate_each(record, attribute, value) value = value.strip address = Mail::Address.new(value) - record.errors.add(attribute, :invalid) if address.address != value + record.errors.add(attribute, :invalid) if address.address != value || contains_disallowed_characters?(value) rescue Mail::Field::FieldError record.errors.add(attribute, :invalid) end + + private + + def contains_disallowed_characters?(value) + value.include?('%') || value.include?(',') || value.include?('"') + end end diff --git a/docker-compose.yml b/docker-compose.yml index 708bb09476318c..27496774189642 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -59,7 +59,7 @@ services: web: # You can uncomment the following line if you want to not use the prebuilt image, for example if you have local code changes build: . - image: kmyblue:23.0 + image: kmyblue:23.1 restart: always env_file: .env.production command: bundle exec puma -C config/puma.rb @@ -83,7 +83,7 @@ services: build: dockerfile: ./streaming/Dockerfile context: . - image: kmyblue-streaming:23.0 + image: kmyblue-streaming:23.1 restart: always env_file: .env.production command: node ./streaming/index.js @@ -101,7 +101,7 @@ services: sidekiq: build: . - image: kmyblue:23.0 + image: kmyblue:23.1 restart: always env_file: .env.production command: bundle exec sidekiq diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index 88514116173426..96581b7270a6ce 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,7 +13,7 @@ def kmyblue_major end def kmyblue_minor - 0 + 1 end def kmyblue_flag @@ -35,7 +35,7 @@ def patch end def default_prerelease - 'alpha.6' + 'alpha.7' end def prerelease diff --git a/lib/tasks/dev.rake b/lib/tasks/dev.rake index b1a144f64cdd33..782758cd1946cf 100644 --- a/lib/tasks/dev.rake +++ b/lib/tasks/dev.rake @@ -441,7 +441,7 @@ namespace :dev do text: 'This post has a manual quote policy', account: remote_account, visibility: :public, - quote_approval_policy: Status::QUOTE_APPROVAL_POLICY_FLAGS[:public] + quote_approval_policy: InteractionPolicy::POLICY_FLAGS[:public] ).find_or_create_by!(id: 10_000_030) end end diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 66f6245d6dc48e..79902b2029a0a3 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -27,6 +27,12 @@ namespace :mastodon do q.messages[:valid?] = 'Invalid domain. If you intend to use unicode characters, enter punycode here' end + if env['LOCAL_DOMAIN'].include?('mastodon') || env['LOCAL_DOMAIN'].include?('mstdn') + prompt.warn 'The Mastodon name is a trademark and its use is restricted.' + prompt.warn 'You can read the trademark policy at https://joinmastodon.org/trademark' + next prompt.warn 'Nothing saved. Bye!' if prompt.no?('Continue anyway?') + end + prompt.say "\n" prompt.say('Single user mode disables registrations and redirects the landing page to your public profile.') diff --git a/package.json b/package.json index 5f5b68e7f51b64..361b02614e04c4 100644 --- a/package.json +++ b/package.json @@ -61,7 +61,7 @@ "@vitejs/plugin-react": "^5.0.0", "arrow-key-navigation": "^1.2.0", "async-mutex": "^0.5.0", - "axios": "^1.4.0", + "axios": "^1.15.0", "babel-plugin-formatjs": "^10.5.37", "babel-plugin-transform-react-remove-prop-types": "^0.4.24", "baseline-browser-mapping": "^2.8.30", @@ -85,11 +85,11 @@ "hoist-non-react-statics": "^3.3.2", "http-link-header": "^1.1.1", "idb": "^8.0.3", - "immutable": "^4.3.0", + "immutable": "^4.3.7", "intl-messageformat": "^10.7.16", "js-yaml": "^4.1.0", "lande": "^1.0.10", - "lodash": "^4.17.21", + "lodash": "4.18.1", "marky": "^1.2.5", "path-complete-extname": "^1.0.0", "postcss-preset-env": "^11.0.0", diff --git a/spec/requests/api/v1/statuses_spec.rb b/spec/requests/api/v1/statuses_spec.rb index 029bbdb1cd2f88..d7d16b1e03fcae 100644 --- a/spec/requests/api/v1/statuses_spec.rb +++ b/spec/requests/api/v1/statuses_spec.rb @@ -406,6 +406,56 @@ end end + context 'with a quote in an unlisted message' do + let!(:quoted_status) { Fabricate(:status, quote_approval_policy: InteractionPolicy::POLICY_FLAGS[:public] << 16) } + let(:params) do + { + status: 'Hello, this is a quote', + quoted_status_id: quoted_status.id, + visibility: 'unlisted', + } + end + + it 'returns a quote post, as well as rate limit headers', :aggregate_failures do + expect { subject }.to change(user.account.statuses, :count).by(1) + + expect(response).to have_http_status(200) + expect(response.content_type) + .to start_with('application/json') + expect(response.parsed_body[:quote]).to be_present + expect(response.headers['X-RateLimit-Limit']).to eq RateLimiter::FAMILIES[:statuses][:limit].to_s + expect(response.headers['X-RateLimit-Remaining']).to eq (RateLimiter::FAMILIES[:statuses][:limit] - 1).to_s + end + + context 'when the quoter is blocked by the quotee' do + before do + quoted_status.account.block!(user.account) + end + + it 'returns an error and does not create a post', :aggregate_failures do + expect { subject }.to_not change(user.account.statuses, :count) + + expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') + end + end + + context 'when the quotee is blocked by the quoter' do + before do + user.account.block!(quoted_status.account) + end + + it 'returns an error and does not create a post', :aggregate_failures do + expect { subject }.to_not change(user.account.statuses, :count) + + expect(response).to have_http_status(404) + expect(response.content_type) + .to start_with('application/json') + end + end + end + context 'with a quote of a reblog' do let(:quoted_status) { Fabricate(:status, quote_approval_policy: InteractionPolicy::POLICY_FLAGS[:public] << 16) } let(:reblog) { Fabricate(:status, reblog: quoted_status) } diff --git a/yarn.lock b/yarn.lock index 9a0f058e1ec428..a10b31e7a64e1c 100644 --- a/yarn.lock +++ b/yarn.lock @@ -397,7 +397,18 @@ __metadata: languageName: node linkType: hard -"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7, @babel/parser@npm:^7.24.4, @babel/parser@npm:^7.28.5, @babel/parser@npm:^7.28.6, @babel/parser@npm:^7.29.0": +"@babel/parser@npm:^7.1.0, @babel/parser@npm:^7.20.7": + version: 7.28.4 + resolution: "@babel/parser@npm:7.28.4" + dependencies: + "@babel/types": "npm:^7.28.4" + bin: + parser: ./bin/babel-parser.js + checksum: 10c0/58b239a5b1477ac7ed7e29d86d675cc81075ca055424eba6485872626db2dc556ce63c45043e5a679cd925e999471dba8a3ed4864e7ab1dbf64306ab72c52707 + languageName: node + linkType: hard + +"@babel/parser@npm:^7.24.4, @babel/parser@npm:^7.28.5, @babel/parser@npm:^7.28.6, @babel/parser@npm:^7.29.0": version: 7.29.0 resolution: "@babel/parser@npm:7.29.0" dependencies: @@ -2992,7 +3003,7 @@ __metadata: "@vitest/ui": "npm:^4.1.0" arrow-key-navigation: "npm:^1.2.0" async-mutex: "npm:^0.5.0" - axios: "npm:^1.4.0" + axios: "npm:^1.15.0" babel-plugin-formatjs: "npm:^10.5.37" babel-plugin-transform-react-remove-prop-types: "npm:^0.4.24" baseline-browser-mapping: "npm:^2.8.30" @@ -3030,12 +3041,12 @@ __metadata: http-link-header: "npm:^1.1.1" husky: "npm:^9.0.11" idb: "npm:^8.0.3" - immutable: "npm:^4.3.0" + immutable: "npm:^4.3.7" intl-messageformat: "npm:^10.7.16" js-yaml: "npm:^4.1.0" lande: "npm:^1.0.10" lint-staged: "npm:^16.2.6" - lodash: "npm:^4.17.21" + lodash: "npm:4.18.1" marky: "npm:^1.2.5" msw: "npm:^2.12.1" msw-storybook-addon: "npm:^2.0.6" @@ -5795,14 +5806,14 @@ __metadata: languageName: node linkType: hard -"axios@npm:^1.4.0": - version: 1.13.2 - resolution: "axios@npm:1.13.2" +"axios@npm:^1.15.0": + version: 1.15.0 + resolution: "axios@npm:1.15.0" dependencies: - follow-redirects: "npm:^1.15.6" - form-data: "npm:^4.0.4" - proxy-from-env: "npm:^1.1.0" - checksum: 10c0/e8a42e37e5568ae9c7a28c348db0e8cf3e43d06fcbef73f0048669edfe4f71219664da7b6cc991b0c0f01c28a48f037c515263cb79be1f1ae8ff034cd813867b + follow-redirects: "npm:^1.15.11" + form-data: "npm:^4.0.5" + proxy-from-env: "npm:^2.1.0" + checksum: 10c0/47e0f860e98d4d7aa145e89ce0cae00e1fb0f1d2485f065c21fce955ddb1dba4103a46bd0e47acd18a27208a7f62c96249e620db575521b92a968619ab133409 languageName: node linkType: hard @@ -8049,13 +8060,13 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.15.6": - version: 1.15.6 - resolution: "follow-redirects@npm:1.15.6" +"follow-redirects@npm:^1.15.11": + version: 1.16.0 + resolution: "follow-redirects@npm:1.16.0" peerDependenciesMeta: debug: optional: true - checksum: 10c0/9ff767f0d7be6aa6870c82ac79cf0368cd73e01bbc00e9eb1c2a16fbb198ec105e3c9b6628bb98e9f3ac66fe29a957b9645bcb9a490bb7aa0d35f908b6b85071 + checksum: 10c0/a1e2900163e6f1b4d1ed5c221b607f41decbab65534c63fe7e287e40a5d552a6496e7d9d7d976fa4ba77b4c51c11e5e9f683f10b43011ea11e442ff128d0e181 languageName: node linkType: hard @@ -8078,16 +8089,16 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^4.0.4": - version: 4.0.4 - resolution: "form-data@npm:4.0.4" +"form-data@npm:^4.0.5": + version: 4.0.5 + resolution: "form-data@npm:4.0.5" dependencies: asynckit: "npm:^0.4.0" combined-stream: "npm:^1.0.8" es-set-tostringtag: "npm:^2.1.0" hasown: "npm:^2.0.2" mime-types: "npm:^2.1.12" - checksum: 10c0/373525a9a034b9d57073e55eab79e501a714ffac02e7a9b01be1c820780652b16e4101819785e1e18f8d98f0aee866cc654d660a435c378e16a72f2e7cac9695 + checksum: 10c0/dd6b767ee0bbd6d84039db12a0fa5a2028160ffbfaba1800695713b46ae974a5f6e08b3356c3195137f8530dcd9dfcb5d5ae1eeff53d0db1e5aad863b619ce3b languageName: node linkType: hard @@ -8754,10 +8765,10 @@ __metadata: languageName: node linkType: hard -"immutable@npm:^4.0.0-rc.1, immutable@npm:^4.3.0": - version: 4.3.7 - resolution: "immutable@npm:4.3.7" - checksum: 10c0/9b099197081b22f6433003e34929da8ecddbbdc1474cdc8aa3b7669dee4adda349c06143de22def36016d1b6de5322b043eccd7a11db1dad2ca85dad4fff5435 +"immutable@npm:^4.0.0-rc.1, immutable@npm:^4.3.7": + version: 4.3.8 + resolution: "immutable@npm:4.3.8" + checksum: 10c0/3de58996305a0faf6ef3fc0685f996c42653ad757760214f5aec7d4a6b59ea7abb882522c5f9a61776fae88c0b45e08eb77cbded5a4f57745ec7c63f9642e44b languageName: node linkType: hard @@ -9897,7 +9908,14 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.20, lodash@npm:^4.17.21": +"lodash@npm:4.18.1": + version: 4.18.1 + resolution: "lodash@npm:4.18.1" + checksum: 10c0/757228fc68805c59789e82185135cf85f05d0b2d3d54631d680ca79ec21944ec8314d4533639a14b8bcfbd97a517e78960933041a5af17ecb693ec6eecb99a27 + languageName: node + linkType: hard + +"lodash@npm:^4.17.20": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: 10c0/d8cbea072bb08655bb4c989da418994b073a608dffa608b09ac04b43a791b12aeae7cd7ad919aa4c925f33b48490b5cfe6c1f71d827956071dae2e7bb3a6b74c @@ -11026,14 +11044,7 @@ __metadata: languageName: node linkType: hard -"pg-protocol@npm:*": - version: 1.13.0 - resolution: "pg-protocol@npm:1.13.0" - checksum: 10c0/a4e851e6bb8ff404ca19d561cf49b6b0caf45163bd3f289889edaf6c4e9fb25b08fb57f50d37a8cc86007efcf2cbb3dd2372c97a353a546f45eb49ddebc84fa9 - languageName: node - linkType: hard - -"pg-protocol@npm:^1.10.3": +"pg-protocol@npm:*, pg-protocol@npm:^1.10.3": version: 1.10.3 resolution: "pg-protocol@npm:1.10.3" checksum: 10c0/f7ef54708c93ee6d271e37678296fc5097e4337fca91a88a3d99359b78633dbdbf6e983f0adb34b7cdd261b7ec7266deb20c3233bf3dfdb498b3e1098e8750b9 @@ -11838,10 +11849,10 @@ __metadata: languageName: node linkType: hard -"proxy-from-env@npm:^1.1.0": - version: 1.1.0 - resolution: "proxy-from-env@npm:1.1.0" - checksum: 10c0/fe7dd8b1bdbbbea18d1459107729c3e4a2243ca870d26d34c2c1bcd3e4425b7bcc5112362df2d93cc7fb9746f6142b5e272fd1cc5c86ddf8580175186f6ad42b +"proxy-from-env@npm:^2.1.0": + version: 2.1.0 + resolution: "proxy-from-env@npm:2.1.0" + checksum: 10c0/ed01729fd4d094eab619cd7e17ce3698b3413b31eb102c4904f9875e677cd207392795d5b4adee9cec359dfd31c44d5ad7595a3a3ad51c40250e141512281c58 languageName: node linkType: hard @@ -13234,13 +13245,20 @@ __metadata: languageName: node linkType: hard -"source-map@npm:^0.7.3, source-map@npm:^0.7.4": +"source-map@npm:^0.7.3": version: 0.7.6 resolution: "source-map@npm:0.7.6" checksum: 10c0/59f6f05538539b274ba771d2e9e32f6c65451982510564438e048bc1352f019c6efcdc6dd07909b1968144941c14015c2c7d4369fb7c4d7d53ae769716dcc16c languageName: node linkType: hard +"source-map@npm:^0.7.4": + version: 0.7.4 + resolution: "source-map@npm:0.7.4" + checksum: 10c0/dc0cf3768fe23c345ea8760487f8c97ef6fca8a73c83cd7c9bf2fde8bc2c34adb9c0824d6feb14bc4f9e37fb522e18af621543f1289038a66ac7586da29aa7dc + languageName: node + linkType: hard + "source-map@npm:^0.8.0-beta.0": version: 0.8.0-beta.0 resolution: "source-map@npm:0.8.0-beta.0"