Merge commit 'df9e26158d9787859b24bdc276af478abf05e1af' into glitch-soc/merge-upstream

Conflicts:
- `lib/sanitize_ext/sanitize_config.rb`:
  Conflict because glitch-soc has a different list of allowed tags.
  Added upstream's new allowed tags while keeping ours.
- `spec/requests/api/v1/timelines/public_spec.rb`:
  Conflict because of glitch-soc's default settings.
  Updated accordingly.
main
Claire 2024-07-04 17:12:14 +02:00
commit e61a7794f8
31 changed files with 187 additions and 48 deletions

View File

@ -2,6 +2,37 @@
All notable changes to this project will be documented in this file. All notable changes to this project will be documented in this file.
## [4.2.10] - 2024-07-04
### Security
- Fix incorrect permission checking on multiple API endpoints ([GHSA-58x8-3qxw-6hm7](https://github.com/mastodon/mastodon/security/advisories/GHSA-58x8-3qxw-6hm7))
- Fix incorrect authorship checking when processing some activities (CVE-2024-37903, [GHSA-xjvf-fm67-4qc3](https://github.com/mastodon/mastodon/security/advisories/GHSA-xjvf-fm67-4qc3))
- Fix ongoing streaming sessions not being invalidated when application tokens get revoked ([GHSA-vp5r-5pgw-jwqx](https://github.com/mastodon/mastodon/security/advisories/GHSA-vp5r-5pgw-jwqx))
- Update dependencies
### Added
- Add yarn version specification to avoid confusion with Yarn 3 and Yarn 4
### Changed
- Change preview cards generation to skip unusually long URLs ([oneiros](https://github.com/mastodon/mastodon/pull/30854))
- Change search modifiers to be case-insensitive ([Gargron](https://github.com/mastodon/mastodon/pull/30865))
- Change `STATSD_ADDR` handling to emit a warning rather than crashing if the address is unreachable ([timothyjrogers](https://github.com/mastodon/mastodon/pull/30691))
- Change PWA start URL from `/home` to `/` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/27377))
### Removed
- Removed dependency on `posix-spawn` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/18559))
### Fixed
- Fix scheduled statuses scheduled in less than 5 minutes being immediately published ([danielmbrasil](https://github.com/mastodon/mastodon/pull/30584))
- Fix encoding detection for link cards ([oneiros](https://github.com/mastodon/mastodon/pull/30780))
- Fix `/admin/accounts/:account_id/statuses/:id` for edited posts with media attachments ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30819))
- Fix duplicate `@context` attribute in user archive export ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/30653))
## [4.2.9] - 2024-05-30 ## [4.2.9] - 2024-05-30
### Security ### Security

View File

@ -6,6 +6,7 @@ class Api::V1::ScheduledStatusesController < Api::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, except: [:update, :destroy] before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, except: [:update, :destroy]
before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:update, :destroy] before_action -> { doorkeeper_authorize! :write, :'write:statuses' }, only: [:update, :destroy]
before_action :require_user!
before_action :set_statuses, only: :index before_action :set_statuses, only: :index
before_action :set_status, except: :index before_action :set_status, except: :index

View File

@ -2,6 +2,7 @@
class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseController class Api::V1::Statuses::TranslationsController < Api::V1::Statuses::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' } before_action -> { doorkeeper_authorize! :read, :'read:statuses' }
before_action :require_user!
before_action :set_translation before_action :set_translation
rescue_from TranslationService::NotConfiguredError, with: :not_found rescue_from TranslationService::NotConfiguredError, with: :not_found

View File

@ -3,8 +3,14 @@
class Api::V1::Timelines::BaseController < Api::BaseController class Api::V1::Timelines::BaseController < Api::BaseController
after_action :insert_pagination_headers, unless: -> { @statuses.empty? } after_action :insert_pagination_headers, unless: -> { @statuses.empty? }
before_action :require_user!, if: :require_auth?
private private
def require_auth?
!Setting.timeline_preview
end
def pagination_collection def pagination_collection
@statuses @statuses
end end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Timelines::LinkController < Api::V1::Timelines::BaseController class Api::V1::Timelines::LinkController < Api::V1::Timelines::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth? before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :set_preview_card before_action :set_preview_card
before_action :set_statuses before_action :set_statuses
@ -17,10 +17,6 @@ class Api::V1::Timelines::LinkController < Api::V1::Timelines::BaseController
private private
def require_auth?
!Setting.timeline_preview
end
def set_preview_card def set_preview_card
@preview_card = PreviewCard.joins(:trend).merge(PreviewCardTrend.allowed).find_by!(url: params[:url]) @preview_card = PreviewCard.joins(:trend).merge(PreviewCardTrend.allowed).find_by!(url: params[:url])
end end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
before_action :require_user!, only: [:show], if: :require_auth? before_action -> { authorize_if_got_token! :read, :'read:statuses' }
PERMITTED_PARAMS = %i(local remote limit only_media allow_local_only).freeze PERMITTED_PARAMS = %i(local remote limit only_media allow_local_only).freeze
@ -13,10 +13,6 @@ class Api::V1::Timelines::PublicController < Api::V1::Timelines::BaseController
private private
def require_auth?
!Setting.timeline_preview
end
def load_statuses def load_statuses
preloaded_public_statuses_page preloaded_public_statuses_page
end end

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController class Api::V1::Timelines::TagController < Api::V1::Timelines::BaseController
before_action -> { doorkeeper_authorize! :read, :'read:statuses' }, only: :show, if: :require_auth? before_action -> { authorize_if_got_token! :read, :'read:statuses' }
before_action :load_tag before_action :load_tag
PERMITTED_PARAMS = %i(local limit only_media).freeze PERMITTED_PARAMS = %i(local limit only_media).freeze

View File

@ -17,6 +17,7 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
def destroy def destroy
Web::PushSubscription.unsubscribe_for(params[:id], current_resource_owner) Web::PushSubscription.unsubscribe_for(params[:id], current_resource_owner)
Doorkeeper::Application.find_by(id: params[:id])&.close_streaming_sessions(current_resource_owner)
super super
end end

View File

@ -224,7 +224,7 @@
"domain_pill.their_server": "O seu fogar dixital, onde están as súas publicacións.", "domain_pill.their_server": "O seu fogar dixital, onde están as súas publicacións.",
"domain_pill.their_username": "O seu identificador único no seu servidor. É posible atopar usuarias co mesmo nome de usuaria en diferentes servidores.", "domain_pill.their_username": "O seu identificador único no seu servidor. É posible atopar usuarias co mesmo nome de usuaria en diferentes servidores.",
"domain_pill.username": "Nome de usuaria", "domain_pill.username": "Nome de usuaria",
"domain_pill.whats_in_a_handle": "Que é o alcume?", "domain_pill.whats_in_a_handle": "As partes do alcume?",
"domain_pill.who_they_are": "O alcume dinos quen é esa persoa e onde está, para que poidas interactuar con ela en toda a web social de <button>plataformas ActivityPub</button>.", "domain_pill.who_they_are": "O alcume dinos quen é esa persoa e onde está, para que poidas interactuar con ela en toda a web social de <button>plataformas ActivityPub</button>.",
"domain_pill.who_you_are": "Como o teu alcume informa de quen es e onde estás, as persoas poden interactuar contigo desde toda a web social de <button>plataformas ActivityPub</button>.", "domain_pill.who_you_are": "Como o teu alcume informa de quen es e onde estás, as persoas poden interactuar contigo desde toda a web social de <button>plataformas ActivityPub</button>.",
"domain_pill.your_handle": "O teu alcume:", "domain_pill.your_handle": "O teu alcume:",

View File

@ -35,7 +35,9 @@
"account.follow_back": "לעקוב בחזרה", "account.follow_back": "לעקוב בחזרה",
"account.followers": "עוקבים", "account.followers": "עוקבים",
"account.followers.empty": "אף אחד לא עוקב אחר המשתמש הזה עדיין.", "account.followers.empty": "אף אחד לא עוקב אחר המשתמש הזה עדיין.",
"account.followers_counter": "{count, plural,one {עוקב אחד} other {{count} עוקבים}}",
"account.following": "נעקבים", "account.following": "נעקבים",
"account.following_counter": "{count, plural,one {עוקב אחרי {count}}other {עוקב אחרי {count}}}",
"account.follows.empty": "משתמש זה עדיין לא עוקב אחרי אף אחד.", "account.follows.empty": "משתמש זה עדיין לא עוקב אחרי אף אחד.",
"account.go_to_profile": "מעבר לפרופיל", "account.go_to_profile": "מעבר לפרופיל",
"account.hide_reblogs": "להסתיר הידהודים מאת @{name}", "account.hide_reblogs": "להסתיר הידהודים מאת @{name}",
@ -61,6 +63,7 @@
"account.requested_follow": "{name} ביקשו לעקוב אחריך", "account.requested_follow": "{name} ביקשו לעקוב אחריך",
"account.share": "שתף את הפרופיל של @{name}", "account.share": "שתף את הפרופיל של @{name}",
"account.show_reblogs": "הצג הדהודים מאת @{name}", "account.show_reblogs": "הצג הדהודים מאת @{name}",
"account.statuses_counter": "{count, plural, one {הודעה אחת} two {הודעותיים} many {{count} הודעות} other {{count} הודעות}}",
"account.unblock": "להסיר חסימה ל- @{name}", "account.unblock": "להסיר חסימה ל- @{name}",
"account.unblock_domain": "הסירי את החסימה של קהילת {domain}", "account.unblock_domain": "הסירי את החסימה של קהילת {domain}",
"account.unblock_short": "הסר חסימה", "account.unblock_short": "הסר חסימה",
@ -693,8 +696,11 @@
"server_banner.about_active_users": "משתמשים פעילים בשרת ב־30 הימים האחרונים (משתמשים פעילים חודשיים)", "server_banner.about_active_users": "משתמשים פעילים בשרת ב־30 הימים האחרונים (משתמשים פעילים חודשיים)",
"server_banner.active_users": "משתמשים פעילים", "server_banner.active_users": "משתמשים פעילים",
"server_banner.administered_by": "מנוהל ע\"י:", "server_banner.administered_by": "מנוהל ע\"י:",
"server_banner.is_one_of_many": "{domain} הוא שרת אחד משרתי מסטודון עצמאיים רבים שדרגם תוכלו להשתתף בפדיוורס (רשת חברתית מבוזרת).",
"server_banner.server_stats": "סטטיסטיקות שרת:", "server_banner.server_stats": "סטטיסטיקות שרת:",
"sign_in_banner.create_account": "יצירת חשבון", "sign_in_banner.create_account": "יצירת חשבון",
"sign_in_banner.follow_anyone": "תוכלו לעקוב אחרי כל משמתמש בפדיוורס ולקרוא הכל לפי סדר הפרסום בציר הזמן. אין אלגוריתמים, פרסומות, או קליקבייט מטעם בעלי הרשת.",
"sign_in_banner.mastodon_is": "מסטודון הוא הדרך הטובה ביותר לעקוב אחרי מה שקורה.",
"sign_in_banner.sign_in": "התחברות", "sign_in_banner.sign_in": "התחברות",
"sign_in_banner.sso_redirect": "התחברות/הרשמה", "sign_in_banner.sso_redirect": "התחברות/הרשמה",
"status.admin_account": "פתח/י ממשק ניהול עבור @{name}", "status.admin_account": "פתח/י ממשק ניהול עבור @{name}",
@ -771,7 +777,7 @@
"timeline_hint.resources.followers": "עוקבים", "timeline_hint.resources.followers": "עוקבים",
"timeline_hint.resources.follows": "נעקבים", "timeline_hint.resources.follows": "נעקבים",
"timeline_hint.resources.statuses": "הודעות ישנות יותר", "timeline_hint.resources.statuses": "הודעות ישנות יותר",
"trends.counter_by_accounts": "{count, plural, one {אדם {count}} other {{count} א.נשים}} {days, plural, one {מאז אתמול} two {ביומיים האחרונים} other {במשך {days} הימים האחרונים}}", "trends.counter_by_accounts": "{count, plural, one {אדם אחד} other {{count} א.נשים}} {days, plural, one {מאז אתמול} two {ביומיים האחרונים} other {במשך {days} הימים האחרונים}}",
"trends.trending_now": "נושאים חמים", "trends.trending_now": "נושאים חמים",
"ui.beforeunload": "הטיוטא תאבד אם תעזבו את מסטודון.", "ui.beforeunload": "הטיוטא תאבד אם תעזבו את מסטודון.",
"units.short.billion": "{count} מליארד", "units.short.billion": "{count} מליארד",

View File

@ -354,7 +354,7 @@
"home.pending_critical_update.link": "Vider actualisationes", "home.pending_critical_update.link": "Vider actualisationes",
"home.pending_critical_update.title": "Actualisation de securitate critic disponibile!", "home.pending_critical_update.title": "Actualisation de securitate critic disponibile!",
"home.show_announcements": "Monstrar annuncios", "home.show_announcements": "Monstrar annuncios",
"interaction_modal.description.favourite": "Con un conto sur Mastodon, tu pote marcar iste message como favorite pro informar le autor que tu lo apprecia e salveguarda pro plus tarde.", "interaction_modal.description.favourite": "Con un conto sur Mastodon, tu pote marcar iste message como favorite pro informar le autor que tu lo apprecia e lo salva pro plus tarde.",
"interaction_modal.description.follow": "Con un conto sur Mastodon, tu pote sequer {name} e reciper su messages in tu fluxo de initio.", "interaction_modal.description.follow": "Con un conto sur Mastodon, tu pote sequer {name} e reciper su messages in tu fluxo de initio.",
"interaction_modal.description.reblog": "Con un conto sur Mastodon, tu pote impulsar iste message pro condivider lo con tu proprie sequitores.", "interaction_modal.description.reblog": "Con un conto sur Mastodon, tu pote impulsar iste message pro condivider lo con tu proprie sequitores.",
"interaction_modal.description.reply": "Con un conto sur Mastodon, tu pote responder a iste message.", "interaction_modal.description.reply": "Con un conto sur Mastodon, tu pote responder a iste message.",
@ -764,7 +764,7 @@
"status.unmute_conversation": "Non plus silentiar conversation", "status.unmute_conversation": "Non plus silentiar conversation",
"status.unpin": "Disfixar del profilo", "status.unpin": "Disfixar del profilo",
"subscribed_languages.lead": "Solmente le messages in le linguas seligite apparera in tu chronologias de initio e de listas post le cambiamento. Selige necun pro reciper messages in tote le linguas.", "subscribed_languages.lead": "Solmente le messages in le linguas seligite apparera in tu chronologias de initio e de listas post le cambiamento. Selige necun pro reciper messages in tote le linguas.",
"subscribed_languages.save": "Salveguardar le cambiamentos", "subscribed_languages.save": "Salvar le cambiamentos",
"subscribed_languages.target": "Cambiar le linguas subscribite pro {target}", "subscribed_languages.target": "Cambiar le linguas subscribite pro {target}",
"tabs_bar.home": "Initio", "tabs_bar.home": "Initio",
"tabs_bar.notifications": "Notificationes", "tabs_bar.notifications": "Notificationes",

View File

@ -35,7 +35,9 @@
"account.follow_back": "Uzvrati praćenje", "account.follow_back": "Uzvrati praćenje",
"account.followers": "Pratioci", "account.followers": "Pratioci",
"account.followers.empty": "Još uvek niko ne prati ovog korisnika.", "account.followers.empty": "Još uvek niko ne prati ovog korisnika.",
"account.followers_counter": "{count, plural, one {{counter} pratilac} few {{counter} pratioca} other {{counter} pratilaca}}",
"account.following": "Prati", "account.following": "Prati",
"account.following_counter": "{count, plural, one {{counter} prati} few {{counter} prati} other {{counter} prati}}",
"account.follows.empty": "Ovaj korisnik još uvek nikog ne prati.", "account.follows.empty": "Ovaj korisnik još uvek nikog ne prati.",
"account.go_to_profile": "Idi na profil", "account.go_to_profile": "Idi na profil",
"account.hide_reblogs": "Sakrij podržavanja @{name}", "account.hide_reblogs": "Sakrij podržavanja @{name}",
@ -61,6 +63,7 @@
"account.requested_follow": "{name} je zatražio da vas prati", "account.requested_follow": "{name} je zatražio da vas prati",
"account.share": "Podeli profil korisnika @{name}", "account.share": "Podeli profil korisnika @{name}",
"account.show_reblogs": "Prikaži podržavanja od korisnika @{name}", "account.show_reblogs": "Prikaži podržavanja od korisnika @{name}",
"account.statuses_counter": "{count, plural, one {{counter} objava} few {{counter} objave} other {{counter} objava}}",
"account.unblock": "Odblokiraj korisnika @{name}", "account.unblock": "Odblokiraj korisnika @{name}",
"account.unblock_domain": "Odblokiraj domen {domain}", "account.unblock_domain": "Odblokiraj domen {domain}",
"account.unblock_short": "Odblokiraj", "account.unblock_short": "Odblokiraj",

View File

@ -35,7 +35,9 @@
"account.follow_back": "Узврати праћење", "account.follow_back": "Узврати праћење",
"account.followers": "Пратиоци", "account.followers": "Пратиоци",
"account.followers.empty": "Још увек нико не прати овог корисника.", "account.followers.empty": "Још увек нико не прати овог корисника.",
"account.followers_counter": "{count, plural, one {{counter} пратилац} few {{counter} пратиоца} other {{counter} пратилаца}}",
"account.following": "Прати", "account.following": "Прати",
"account.following_counter": "{count, plural, one {{counter} прати} few {{counter} прати} other {{counter} прати}}",
"account.follows.empty": "Овај корисник још увек никог не прати.", "account.follows.empty": "Овај корисник још увек никог не прати.",
"account.go_to_profile": "Иди на профил", "account.go_to_profile": "Иди на профил",
"account.hide_reblogs": "Сакриј подржавања од @{name}", "account.hide_reblogs": "Сакриј подржавања од @{name}",
@ -61,6 +63,7 @@
"account.requested_follow": "{name} је затражио да вас прати", "account.requested_follow": "{name} је затражио да вас прати",
"account.share": "Подели профил корисника @{name}", "account.share": "Подели профил корисника @{name}",
"account.show_reblogs": "Прикажи подржавања од корисника @{name}", "account.show_reblogs": "Прикажи подржавања од корисника @{name}",
"account.statuses_counter": "{count, plural, one {{counter} објава} few {{counter} објаве} other {{counter} објава}}",
"account.unblock": "Одблокирај корисника @{name}", "account.unblock": "Одблокирај корисника @{name}",
"account.unblock_domain": "Одблокирај домен {domain}", "account.unblock_domain": "Одблокирај домен {domain}",
"account.unblock_short": "Одблокирај", "account.unblock_short": "Одблокирај",

View File

@ -104,7 +104,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
def find_existing_status def find_existing_status
status = status_from_uri(object_uri) status = status_from_uri(object_uri)
status ||= Status.find_by(uri: @object['atomUri']) if @object['atomUri'].present? status ||= Status.find_by(uri: @object['atomUri']) if @object['atomUri'].present?
status status if status&.account_id == @account.id
end end
def process_status_params def process_status_params

View File

@ -16,7 +16,7 @@ module ApplicationExtension
# dependent: delete_all, which means the ActiveRecord callback in # dependent: delete_all, which means the ActiveRecord callback in
# AccessTokenExtension is not run, so instead we manually announce to # AccessTokenExtension is not run, so instead we manually announce to
# streaming that these tokens are being deleted. # streaming that these tokens are being deleted.
before_destroy :push_to_streaming_api, prepend: true before_destroy :close_streaming_sessions, prepend: true
end end
def confirmation_redirect_uri def confirmation_redirect_uri
@ -29,10 +29,12 @@ module ApplicationExtension
redirect_uri.split redirect_uri.split
end end
def push_to_streaming_api def close_streaming_sessions(resource_owner = nil)
# TODO: #28793 Combine into a single topic # TODO: #28793 Combine into a single topic
payload = Oj.dump(event: :kill) payload = Oj.dump(event: :kill)
access_tokens.in_batches do |tokens| scope = access_tokens
scope = scope.where(resource_owner_id: resource_owner.id) unless resource_owner.nil?
scope.in_batches do |tokens|
redis.pipelined do |pipeline| redis.pipelined do |pipeline|
tokens.ids.each do |id| tokens.ids.each do |id|
pipeline.publish("timeline:access_token:#{id}", payload) pipeline.publish("timeline:access_token:#{id}", payload)

View File

@ -574,7 +574,7 @@ ia:
enabled: Activate enabled: Activate
inbox_url: URL del repetitor inbox_url: URL del repetitor
pending: Attende le approbation del repetitor pending: Attende le approbation del repetitor
save_and_enable: Salveguardar e activar save_and_enable: Salvar e activar
setup: Crear un connexion con un repetitor setup: Crear un connexion con un repetitor
signatures_not_enabled: Le repetitores pote non functionar correctemente durante que le modo secur o le modo de federation limitate es activate signatures_not_enabled: Le repetitores pote non functionar correctemente durante que le modo secur o le modo de federation limitate es activate
status: Stato status: Stato
@ -1276,7 +1276,7 @@ ia:
other: "%{count} messages individual celate" other: "%{count} messages individual celate"
title: Filtros title: Filtros
new: new:
save: Salveguardar nove filtro save: Salvar nove filtro
title: Adder nove filtro title: Adder nove filtro
statuses: statuses:
back_to_filter: Retro al filtro back_to_filter: Retro al filtro
@ -1294,14 +1294,14 @@ ia:
one: "<strong>%{count}</strong> elemento correspondente al recerca es seligite." one: "<strong>%{count}</strong> elemento correspondente al recerca es seligite."
other: Tote le <strong>%{count}</strong> elementos correspondente al recerca es seligite. other: Tote le <strong>%{count}</strong> elementos correspondente al recerca es seligite.
cancel: Cancellar cancel: Cancellar
changes_saved_msg: Cambios salveguardate con successo! changes_saved_msg: Le cambiamentos ha essite salvate!
confirm: Confirmar confirm: Confirmar
copy: Copiar copy: Copiar
delete: Deler delete: Deler
deselect: Deseliger toto deselect: Deseliger toto
none: Necun none: Necun
order_by: Ordinar per order_by: Ordinar per
save_changes: Salvar le cambios save_changes: Salvar le cambiamentos
select_all_matching_items: select_all_matching_items:
one: Selige %{count} elemento correspondente a tu recerca. one: Selige %{count} elemento correspondente a tu recerca.
other: Selige %{count} elementos correspondente a tu recerca. other: Selige %{count} elementos correspondente a tu recerca.

View File

@ -81,7 +81,7 @@ ja:
backups_retention_period: ユーザーには、後でダウンロードするために投稿のアーカイブを生成する機能があります。正の値に設定すると、これらのアーカイブは指定された日数後に自動的にストレージから削除されます。 backups_retention_period: ユーザーには、後でダウンロードするために投稿のアーカイブを生成する機能があります。正の値に設定すると、これらのアーカイブは指定された日数後に自動的にストレージから削除されます。
bootstrap_timeline_accounts: これらのアカウントは、新しいユーザー向けのおすすめユーザーの一番上にピン留めされます。 bootstrap_timeline_accounts: これらのアカウントは、新しいユーザー向けのおすすめユーザーの一番上にピン留めされます。
closed_registrations_message: アカウント作成を停止している時に表示されます closed_registrations_message: アカウント作成を停止している時に表示されます
content_cache_retention_period: 他のサーバーからのすべての投稿(ブーストや返信を含む)は、指定された日数が経過すると、ローカルユーザーとのやりとりに関係なく削除されます。これには、ローカルユーザーがブックマークやお気に入りとして登録した投稿も含まれます。異なるサーバーのユーザー間の非公開な変身も失われ、復元することは不可能です。この設定の使用は特別な目的のインスタンスのためのものであり、一般的な目的のサーバーで使用するした場合、多くのユーザーの期待を裏切ることになります。 content_cache_retention_period: 他のサーバーからのすべての投稿(ブーストや返信を含む)は、指定された日数が経過すると、ローカルユーザーとのやりとりに関係なく削除されます。これには、ローカルユーザーがブックマークやお気に入りとして登録した投稿も含まれます。異なるサーバーのユーザー間の非公開な返信も失われ、復元することは不可能です。この設定の使用は特別な目的のインスタンスのためのものであり、一般的な目的のサーバーで使用した場合、多くのユーザーの期待を裏切ることになります。
custom_css: ウェブ版のMastodonでカスタムスタイルを適用できます。 custom_css: ウェブ版のMastodonでカスタムスタイルを適用できます。
favicon: デフォルトのMastodonのブックマークアイコンを独自のアイコンで上書きします。WEBP、PNG、GIF、JPGが利用可能です。 favicon: デフォルトのMastodonのブックマークアイコンを独自のアイコンで上書きします。WEBP、PNG、GIF、JPGが利用可能です。
mascot: 上級者向けWebインターフェースのイラストを上書きします。 mascot: 上級者向けWebインターフェースのイラストを上書きします。

View File

@ -58,7 +58,7 @@ services:
web: web:
build: . build: .
image: ghcr.io/mastodon/mastodon:v4.2.9 image: ghcr.io/mastodon/mastodon:v4.2.10
restart: always restart: always
env_file: .env.production env_file: .env.production
command: bundle exec puma -C config/puma.rb command: bundle exec puma -C config/puma.rb
@ -79,7 +79,7 @@ services:
streaming: streaming:
build: . build: .
image: ghcr.io/mastodon/mastodon:v4.2.9 image: ghcr.io/mastodon/mastodon:v4.2.10
restart: always restart: always
env_file: .env.production env_file: .env.production
command: node ./streaming command: node ./streaming
@ -97,7 +97,7 @@ services:
sidekiq: sidekiq:
build: . build: .
image: ghcr.io/mastodon/mastodon:v4.2.9 image: ghcr.io/mastodon/mastodon:v4.2.10
restart: always restart: always
env_file: .env.production env_file: .env.production
command: bundle exec sidekiq command: bundle exec sidekiq

View File

@ -17,7 +17,7 @@ module Mastodon
end end
def default_prerelease def default_prerelease
'alpha.4' 'alpha.5'
end end
def prerelease def prerelease

View File

@ -75,7 +75,7 @@ class Sanitize
end end
MASTODON_STRICT = freeze_config( MASTODON_STRICT = freeze_config(
elements: %w(p br span a abbr del pre blockquote code b strong u sub sup i em h1 h2 h3 h4 h5 ul ol li), elements: %w(p br span a abbr del pre blockquote code b strong u sub sup i em h1 h2 h3 h4 h5 ul ol li ruby rt rp),
attributes: { attributes: {
'a' => %w(href rel class title translate), 'a' => %w(href rel class title translate),

View File

@ -50,9 +50,11 @@ describe Oauth::AuthorizedApplicationsController do
let!(:application) { Fabricate(:application) } let!(:application) { Fabricate(:application) }
let!(:access_token) { Fabricate(:accessible_access_token, application: application, resource_owner_id: user.id) } let!(:access_token) { Fabricate(:accessible_access_token, application: application, resource_owner_id: user.id) }
let!(:web_push_subscription) { Fabricate(:web_push_subscription, user: user, access_token: access_token) } let!(:web_push_subscription) { Fabricate(:web_push_subscription, user: user, access_token: access_token) }
let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) }
before do before do
sign_in user, scope: :user sign_in user, scope: :user
allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub)
post :destroy, params: { id: application.id } post :destroy, params: { id: application.id }
end end
@ -67,5 +69,9 @@ describe Oauth::AuthorizedApplicationsController do
it 'removes the web_push_subscription' do it 'removes the web_push_subscription' do
expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound) expect { web_push_subscription.reload }.to raise_error(ActiveRecord::RecordNotFound)
end end
it 'sends a session kill payload to the streaming server' do
expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", '{"event":"kill"}')
end
end end
end end

View File

@ -147,14 +147,22 @@ describe Settings::ApplicationsController do
end end
describe 'destroy' do describe 'destroy' do
let(:redis_pipeline_stub) { instance_double(Redis::Namespace, publish: nil) }
let!(:access_token) { Fabricate(:accessible_access_token, application: app) }
before do before do
allow(redis).to receive(:pipelined).and_yield(redis_pipeline_stub)
post :destroy, params: { id: app.id } post :destroy, params: { id: app.id }
end end
it 'redirects back to applications page and removes the app' do it 'redirects back to applications page removes the app' do
expect(response).to redirect_to(settings_applications_path) expect(response).to redirect_to(settings_applications_path)
expect(Doorkeeper::Application.find_by(id: app.id)).to be_nil expect(Doorkeeper::Application.find_by(id: app.id)).to be_nil
end end
it 'sends a session kill payload to the streaming server' do
expect(redis_pipeline_stub).to have_received(:publish).with("timeline:access_token:#{access_token.id}", '{"event":"kill"}')
end
end end
describe 'regenerate' do describe 'regenerate' do

View File

@ -41,6 +41,14 @@ RSpec.describe HtmlAwareFormatter do
expect(subject).to_not include 'status__content__spoiler-link' expect(subject).to_not include 'status__content__spoiler-link'
end end
end end
context 'when given text containing ruby tags for east-asian languages' do
let(:text) { '<ruby>明日 <rp>(</rp><rt>Ashita</rt><rp>)</rp></ruby>' }
it 'keeps the ruby tags' do
expect(subject).to eq '<ruby>明日 <rp>(</rp><rt>Ashita</rt><rp>)</rp></ruby>'
end
end
end end
end end
end end

View File

@ -72,6 +72,14 @@ RSpec.describe PlainTextFormatter do
expect(subject).to eq 'Lorem ipsum' expect(subject).to eq 'Lorem ipsum'
end end
end end
context 'when text contains HTML ruby tags' do
let(:status) { Fabricate(:status, account: remote_account, text: '<p>Lorem <ruby>明日 <rp>(</rp><rt>Ashita</rt><rp>)</rp></ruby> ipsum</p>') }
it 'strips the comment' do
expect(subject).to eq 'Lorem 明日 (Ashita) ipsum'
end
end
end end
end end
end end

View File

@ -16,6 +16,10 @@ describe Sanitize::Config do
expect(Sanitize.fragment('<p>Check out:</p><ol start="3" reversed=""><li>Foo</li><li>Bar</li></ol>', subject)).to eq '<p>Check out:</p><ol start="3" reversed=""><li>Foo</li><li>Bar</li></ol>' expect(Sanitize.fragment('<p>Check out:</p><ol start="3" reversed=""><li>Foo</li><li>Bar</li></ol>', subject)).to eq '<p>Check out:</p><ol start="3" reversed=""><li>Foo</li><li>Bar</li></ol>'
end end
it 'keeps ruby tags' do
expect(Sanitize.fragment('<p><ruby>明日 <rp>(</rp><rt>Ashita</rt><rp>)</rp></ruby></p>', subject)).to eq '<p><ruby>明日 <rp>(</rp><rt>Ashita</rt><rp>)</rp></ruby></p>'
end
it 'removes a without href' do it 'removes a without href' do
expect(Sanitize.fragment('<a>Test</a>', subject)).to eq 'Test' expect(Sanitize.fragment('<a>Test</a>', subject)).to eq 'Test'
end end

View File

@ -25,6 +25,17 @@ describe 'Scheduled Statuses' do
it_behaves_like 'forbidden for wrong scope', 'write write:statuses' it_behaves_like 'forbidden for wrong scope', 'write write:statuses'
end end
context 'with an application token' do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: 'read:statuses') }
it 'returns http unprocessable entity' do
get api_v1_scheduled_statuses_path, headers: headers
expect(response)
.to have_http_status(422)
end
end
context 'with correct scope' do context 'with correct scope' do
let(:scopes) { 'read:statuses' } let(:scopes) { 'read:statuses' }

View File

@ -8,6 +8,22 @@ describe 'API V1 Statuses Translations' do
let(:scopes) { 'read:statuses' } let(:scopes) { 'read:statuses' }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } } let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
context 'with an application token' do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: scopes) }
describe 'POST /api/v1/statuses/:status_id/translate' do
let(:status) { Fabricate(:status, account: user.account, text: 'Hola', language: 'es') }
before do
post "/api/v1/statuses/#{status.id}/translate", headers: headers
end
it 'returns http unprocessable entity' do
expect(response).to have_http_status(422)
end
end
end
context 'with an oauth token' do context 'with an oauth token' do
describe 'POST /api/v1/statuses/:status_id/translate' do describe 'POST /api/v1/statuses/:status_id/translate' do
let(:status) { Fabricate(:status, account: user.account, text: 'Hola', language: 'es') } let(:status) { Fabricate(:status, account: user.account, text: 'Hola', language: 'es') }

View File

@ -41,6 +41,8 @@ describe 'Link' do
end end
end end
it_behaves_like 'forbidden for wrong scope', 'profile'
context 'when there is no preview card' do context 'when there is no preview card' do
let(:preview_card) { nil } let(:preview_card) { nil }
@ -80,13 +82,25 @@ describe 'Link' do
Form::AdminSettings.new(timeline_preview: false).save Form::AdminSettings.new(timeline_preview: false).save
end end
context 'when the user is not authenticated' do it_behaves_like 'forbidden for wrong scope', 'profile'
context 'without an authentication token' do
let(:headers) { {} } let(:headers) { {} }
it 'returns http unauthorized' do it 'returns http unprocessable entity' do
subject subject
expect(response).to have_http_status(401) expect(response).to have_http_status(422)
end
end
context 'with an application access token, not bound to a user' do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: scopes) }
it 'returns http unprocessable entity' do
subject
expect(response).to have_http_status(422)
end end
end end

View File

@ -38,6 +38,8 @@ describe 'Public' do
Setting.timeline_preview = true Setting.timeline_preview = true
end end
it_behaves_like 'forbidden for wrong scope', 'profile'
context 'with an authorized user' do context 'with an authorized user' do
it_behaves_like 'a successful request to the public timeline' it_behaves_like 'a successful request to the public timeline'
end end
@ -103,13 +105,9 @@ describe 'Public' do
Form::AdminSettings.new(timeline_preview: false).save Form::AdminSettings.new(timeline_preview: false).save
end end
context 'with an authenticated user' do it_behaves_like 'forbidden for wrong scope', 'profile'
let(:expected_statuses) { [local_status, remote_status, media_status] }
it_behaves_like 'a successful request to the public timeline' context 'without an authentication token' do
end
context 'with an unauthenticated user' do
let(:headers) { {} } let(:headers) { {} }
it 'returns http unprocessable entity' do it 'returns http unprocessable entity' do
@ -118,6 +116,22 @@ describe 'Public' do
expect(response).to have_http_status(422) expect(response).to have_http_status(422)
end end
end end
context 'with an application access token, not bound to a user' do
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: nil, scopes: scopes) }
it 'returns http unprocessable entity' do
subject
expect(response).to have_http_status(422)
end
end
context 'with an authenticated user' do
let(:expected_statuses) { [local_status, remote_status, media_status] }
it_behaves_like 'a successful request to the public timeline'
end
end end
end end
end end

View File

@ -34,6 +34,8 @@ RSpec.describe 'Tag' do
let(:params) { {} } let(:params) { {} }
let(:hashtag) { 'life' } let(:hashtag) { 'life' }
it_behaves_like 'forbidden for wrong scope', 'profile'
context 'when given only one hashtag' do context 'when given only one hashtag' do
let(:expected_statuses) { [life_status] } let(:expected_statuses) { [life_status] }
@ -97,13 +99,15 @@ RSpec.describe 'Tag' do
Form::AdminSettings.new(timeline_preview: false).save Form::AdminSettings.new(timeline_preview: false).save
end end
context 'when the user is not authenticated' do it_behaves_like 'forbidden for wrong scope', 'profile'
context 'without an authentication token' do
let(:headers) { {} } let(:headers) { {} }
it 'returns http unauthorized' do it 'returns http unprocessable entity' do
subject subject
expect(response).to have_http_status(401) expect(response).to have_http_status(422)
end end
end end

View File

@ -13163,14 +13163,14 @@ __metadata:
linkType: hard linkType: hard
"pino-http@npm:^10.0.0": "pino-http@npm:^10.0.0":
version: 10.1.0 version: 10.2.0
resolution: "pino-http@npm:10.1.0" resolution: "pino-http@npm:10.2.0"
dependencies: dependencies:
get-caller-file: "npm:^2.0.5" get-caller-file: "npm:^2.0.5"
pino: "npm:^9.0.0" pino: "npm:^9.0.0"
pino-std-serializers: "npm:^7.0.0" pino-std-serializers: "npm:^7.0.0"
process-warning: "npm:^3.0.0" process-warning: "npm:^3.0.0"
checksum: 10c0/d97691f2ee248b0aca0e49169d0c7ca0d4c604ee57b63ae264a6f9914fc7277cace74686d5088a876f8152a8d5b8211af904b2d24a516728a662de0e9cc79e9f checksum: 10c0/0b79cd3602531ee5043693e2a3ccf9d955bd93759e80c0b3a458b95b241f36ca8ebc72c8050b395e9d8fcb9581ebc18ecd6b7dc136526bebe924bc5c5079374d
languageName: node languageName: node
linkType: hard linkType: hard
@ -18515,8 +18515,8 @@ __metadata:
linkType: hard linkType: hard
"ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.17.0": "ws@npm:^8.11.0, ws@npm:^8.12.1, ws@npm:^8.17.0":
version: 8.17.1 version: 8.18.0
resolution: "ws@npm:8.17.1" resolution: "ws@npm:8.18.0"
peerDependencies: peerDependencies:
bufferutil: ^4.0.1 bufferutil: ^4.0.1
utf-8-validate: ">=5.0.2" utf-8-validate: ">=5.0.2"
@ -18525,7 +18525,7 @@ __metadata:
optional: true optional: true
utf-8-validate: utf-8-validate:
optional: true optional: true
checksum: 10c0/f4a49064afae4500be772abdc2211c8518f39e1c959640457dcee15d4488628620625c783902a52af2dd02f68558da2868fd06e6fd0e67ebcd09e6881b1b5bfe checksum: 10c0/25eb33aff17edcb90721ed6b0eb250976328533ad3cd1a28a274bd263682e7296a6591ff1436d6cbc50fa67463158b062f9d1122013b361cec99a05f84680e06
languageName: node languageName: node
linkType: hard linkType: hard