Skip to content

Commit 8a6fa68

Browse files
committed
Merge remote-tracking branch 'parent/stable-4.5' into kb-draft-21.8-lts
2 parents de1d6d6 + ab872f2 commit 8a6fa68

33 files changed

Lines changed: 274 additions & 92 deletions

CHANGELOG.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,26 @@
22

33
All notable changes to this project will be documented in this file.
44

5+
## [4.5.7] - 2026-02-24
6+
7+
### Security
8+
9+
- Reject unconfirmed FASPs (#37926 by @oneiros, [GHSA-qgmm-vr4c-ggjg](https://github.com/mastodon/mastodon/security/advisories/GHSA-qgmm-vr4c-ggjg))
10+
- Re-use custom socket class for FASP requests (#37925 by @oneiros, [GHSA-46w6-g98f-wxqm](https://github.com/mastodon/mastodon/security/advisories/GHSA-46w6-g98f-wxqm))
11+
12+
### Added
13+
14+
- Add `--suspended-only` option to `tootctl emoji purge` (#37828 and #37861 by @ClearlyClaire and @mjankowski)
15+
16+
### Fixed
17+
18+
- Fix emoji data not being properly cached (#37858 by @ChaosExAnima)
19+
- Fix delete & redraft of pending posts (#37839 by @ClearlyClaire)
20+
- Fix processing separate key documents without the ActivityStreams context (#37826 by @ClearlyClaire)
21+
- Fix custom emojis not being purged on domain suspension (#37808 by @ClearlyClaire)
22+
- Fix users without special permissions being able to stream disabled timelines (#37791 by @ClearlyClaire)
23+
- Fix processing of object updates with duplicate hashtags (#37756 by @ClearlyClaire)
24+
525
## [4.5.6] - 2026-02-03
626

727
### Security

app/controllers/api/fasp/base_controller.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ def validate_signature!
4747
provider = nil
4848

4949
Linzer.verify!(request.rack_request, no_older_than: 5.minutes) do |keyid|
50-
provider = Fasp::Provider.find(keyid)
50+
provider = Fasp::Provider.confirmed.find(keyid)
5151
Linzer.new_ed25519_public_key(provider.provider_public_key_pem, keyid)
5252
end
5353

app/helpers/json_ld_helper.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,10 @@ def supported_context?(json)
7878
!json.nil? && equals_or_includes?(json['@context'], ActivityPub::TagManager::CONTEXT)
7979
end
8080

81+
def supported_security_context?(json)
82+
!json.nil? && equals_or_includes?(json['@context'], 'https://w3id.org/security/v1')
83+
end
84+
8185
def unsupported_uri_scheme?(uri)
8286
uri.nil? || !uri.start_with?('http://', 'https://')
8387
end

app/javascript/mastodon/actions/statuses.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,14 +119,15 @@ export function fetchStatusFail(id, error, skipLoading, parentQuotePostId) {
119119
};
120120
}
121121

122-
export function redraft(status, raw_text) {
122+
export function redraft(status, raw_text, quoted_status_id = null) {
123123
return (dispatch, getState) => {
124124
const maxOptions = getState().server.getIn(['server', 'configuration', 'polls', 'max_options']);
125125

126126
dispatch({
127127
type: REDRAFT,
128128
status,
129129
raw_text,
130+
quoted_status_id,
130131
maxOptions,
131132
});
132133
};
@@ -179,7 +180,7 @@ export function deleteStatus(id, withRedraft = false) {
179180
dispatch(importFetchedAccount(response.data.account));
180181

181182
if (withRedraft) {
182-
dispatch(redraft(status, response.data.text));
183+
dispatch(redraft(status, response.data.text, response.data.quote?.quoted_status?.id));
183184
ensureComposeIsVisible(getState);
184185
} else {
185186
dispatch(showAlert({ message: messages.deleteSuccess }));

app/javascript/mastodon/features/emoji/loader.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,19 @@ export async function importEmojiData(localeString: string, path?: string) {
2121
path ??= await localeToPath(locale);
2222
}
2323

24+
// Fix from #37858. Check if we've loaded this path before.
25+
const existing = await loadLatestEtag(locale);
26+
if (existing === path) {
27+
return null;
28+
}
29+
2430
const emojis = await fetchAndCheckEtag<CompactEmoji[]>(locale, path);
2531
if (!emojis) {
2632
return;
2733
}
34+
35+
await putLatestEtag(path, locale); // Fix from #37858. Put the path as the ETag to ensure we don't load the same data again.
36+
2837
const flattenedEmojis: FlatCompactEmoji[] = flattenEmojiData(emojis);
2938
await putEmojiData(flattenedEmojis, locale);
3039
return flattenedEmojis;

app/javascript/mastodon/reducers/compose.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -667,7 +667,7 @@ export const composeReducer = (state = initialState, action) => {
667667
map.set('language', action.status.get('language'));
668668
map.set('markdown', action.status.get('markdown'));
669669
map.set('id', null);
670-
map.set('quoted_status_id', action.status.getIn(['quote', 'quoted_status'], null));
670+
map.set('quoted_status_id', action.quoted_status_id);
671671
// Mastodon-authored posts can be expected to have at most one automatic approval policy
672672
map.set('quote_policy', action.status.getIn(['quote_approval', 'automatic', 0]) || 'nobody');
673673

app/lib/fasp/request.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ def perform_request(verb, path, body: nil)
2929
response = HTTP
3030
.headers(headers)
3131
.use(http_signature: { key:, covered_components: COVERED_COMPONENTS })
32-
.send(verb, url, body:)
32+
.send(verb, url, body:, socket_class: ::Request::Socket)
3333

3434
validate!(response)
3535
@provider.delivery_failure_tracker.track_success!

app/lib/request.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -349,5 +349,5 @@ def check_private_address(_address, _host)
349349
end
350350
end
351351

352-
private_constant :ClientLimit, :Socket, :ProxySocket
352+
private_constant :ClientLimit
353353
end

app/models/fasp/provider.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ class Fasp::Provider < ApplicationRecord
3737
before_create :create_keypair
3838
after_commit :update_remote_capabilities
3939

40+
scope :confirmed, -> { where(confirmed: true) }
4041
scope :with_capability, lambda { |capability_name|
4142
where('fasp_providers.capabilities @> ?::jsonb', "[{\"id\": \"#{capability_name}\", \"enabled\": true}]")
4243
}

app/serializers/rest/base_quote_serializer.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ def state
1313
end
1414

1515
def quoted_status
16-
object.quoted_status if object.accepted? && object.quoted_status.present? && !object.quoted_status&.reblog? && status_filter.filter_state_for_quote != 'unauthorized'
16+
object.quoted_status if (object.accepted? || instance_options[:source_requested]) && object.quoted_status.present? && !object.quoted_status&.reblog? && status_filter.filter_state_for_quote != 'unauthorized'
1717
end
1818

1919
private

0 commit comments

Comments
 (0)