Skip to content

Commit

Permalink
Merge branch 'kb_development' into upstream-20231021
Browse files Browse the repository at this point in the history
  • Loading branch information
kmycode committed Oct 22, 2023
2 parents f9eaaec + bcb3acd commit b992e67
Show file tree
Hide file tree
Showing 25 changed files with 665 additions and 112 deletions.
2 changes: 1 addition & 1 deletion .github/ISSUE_TEMPLATE/1.bug_report.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
name: バグ報告
description: kmyblueのバグ報告
description: kmyblueのバグ報告(ただし情報改竄、秘密情報の漏洩、システムの破損などが発生するバグは、こちらではなく「Security」タブよりセキュリティインシデントとして報告してください)
labels: [bug]
body:
- type: textarea
Expand Down
33 changes: 18 additions & 15 deletions SECURITY.md
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
# Security Policy
# セキュリティポリシー

If you believe you've identified a security vulnerability in Mastodon (a bug that allows something to happen that shouldn't be possible), you can either:
kmyblueのプログラムにおいてセキュリティインシデントを発見した場合、kmyblueに報告してください。

- open a [Github security issue on the Mastodon project](https://github.com/mastodon/mastodon/security/advisories/new)
- reach us at <[email protected]>
kmyblueにセキュリティインシデントを報告する場合、以下の手順を踏んでください。

You should _not_ report such issues on public GitHub issues or in other public spaces to give us time to publish a fix for the issue without exposing Mastodon's users to increased risk.
- [こちらのリンクから新規インシデントを起票してください](https://github.com/kmycode/mastodon/security/advisories/new)
- メール <[email protected]>、または[@askyq@kmy.blue](https://kmy.blue/@askyq)宛に、**セキュリティインシデントを起票したことだけ**を連絡してください。セキュリティインシデントの内容は、絶対に連絡に含めないでください(リンクくらいなら含めていいかな)

## Scope
他のkmyblueフォークの利用者の安全のために少しでも時間稼ぎをしなければいけないので、この問題をIssueを含む公開された場所で記述しないでください。

A "vulnerability in Mastodon" is a vulnerability in the code distributed through our main source code repository on GitHub. Vulnerabilities that are specific to a given installation (e.g. misconfiguration) should be reported to the owner of that installation and not us.
## 範囲

## Supported Versions
こちらが対応できる範囲は、当リポジトリで公開しているソースコードのみとなります。当リポジトリの依存パッケージ内に問題がある場合は、そちらに報告してください。

| Version | Supported |
| ------- | ---------------- |
| 4.2.x | Yes |
| 4.1.x | Yes |
| 4.0.x | Until 2023-10-31 |
| 3.5.x | Until 2023-12-31 |
| < 3.5 | No |
もしあなたに専門知識があり、それが本家Mastodon由来の問題であると信じるに足る根拠がある場合、kmyblueではなくMastodonのほうに報告してください。kmyblueに報告されても、Mastodonより先に修正してしまうことでMastodonにセキュリティリスクを発生させる可能性がありますし、本家Mastodonの対応を待つにしてもkmyblueのほうに来てしまったセキュリティインシデントの対応に困ります(本家がなかなか対応してくれない可能性を考えると削除しづらい)。もし間違ってkmyblueに来た場合、kmyblue開発者の責任で振り分けを行います。

## サポートするバージョン

下記以外のバージョンは、セキュリティインシデントを起票されても対応しません。

- 最新メジャーバージョン、かつ、最新マイナーバージョン
- 最新メジャーバージョンのサポートは、次のメジャーバージョンが出た時点で終了します
- LTS
- LTSのサポートは、次のLTSが出た時点で終了します(ただし移行期間があってもいいと思ってるので、1〜3ヶ月以内ならセキュリティインシデントの程度に応じて対応する可能性があります)
2 changes: 2 additions & 0 deletions app/helpers/context_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,8 @@ module ContextHelper
other_setting: { 'fedibird' => 'http://fedibird.com/ns#', 'otherSetting' => 'fedibird:otherSetting' },
references: { 'fedibird' => 'http://fedibird.com/ns#', 'references' => { '@id' => 'fedibird:references', '@type' => '@id' } },
quote_uri: { 'fedibird' => 'http://fedibird.com/ns#', 'quoteUri' => 'fedibird:quoteUri' },
keywords: { 'schema' => 'http://schema.org#', 'keywords' => 'schema:keywords' },
license: { 'schema' => 'http://schema.org#', 'license' => 'schema:license' },
olm: {
'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId',
'claim' => { '@type' => '@id', '@id' => 'toot:claim' },
Expand Down
1 change: 1 addition & 0 deletions app/lib/activitypub/activity/create.rb
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,7 @@ def process_emoji(tag)
emoji.image_remote_url = custom_emoji_parser.image_remote_url
emoji.license = custom_emoji_parser.license
emoji.is_sensitive = custom_emoji_parser.is_sensitive
emoji.aliases = custom_emoji_parser.aliases
emoji.save
rescue Seahorse::Client::NetworkingError => e
Rails.logger.warn "Error storing emoji: #{e}"
Expand Down
1 change: 1 addition & 0 deletions app/lib/activitypub/activity/like.rb
Original file line number Diff line number Diff line change
Expand Up @@ -127,6 +127,7 @@ def process_emoji(tag)
emoji.image_remote_url = custom_emoji_parser.image_remote_url
emoji.license = custom_emoji_parser.license
emoji.is_sensitive = custom_emoji_parser.is_sensitive
emoji.aliases = custom_emoji_parser.aliases
emoji.save
rescue Seahorse::Client::NetworkingError => e
Rails.logger.warn "Error storing emoji: #{e}"
Expand Down
4 changes: 4 additions & 0 deletions app/lib/activitypub/parser/custom_emoji_parser.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ def shortcode
@json['name']&.delete(':')
end

def aliases
as_array(@json['keywords'])
end

def image_remote_url
@json.dig('icon', 'url')
end
Expand Down
1 change: 1 addition & 0 deletions app/models/account.rb
Original file line number Diff line number Diff line change
Expand Up @@ -399,6 +399,7 @@ def show_emoji_reaction?(account)

def allow_emoji_reaction?(account)
return false if account.nil?
return true unless local? || account.local?

show_emoji_reaction?(account)
end
Expand Down
8 changes: 6 additions & 2 deletions app/serializers/activitypub/emoji_serializer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@
class ActivityPub::EmojiSerializer < ActivityPub::Serializer
include RoutingHelper

context_extensions :emoji
context_extensions :emoji, :license, :keywords

attributes :id, :type, :domain, :name, :is_sensitive, :updated
attributes :id, :type, :domain, :name, :keywords, :is_sensitive, :updated

attribute :license, if: -> { object.license.present? }

Expand All @@ -23,6 +23,10 @@ def domain
object.domain.presence || Rails.configuration.x.local_domain
end

def keywords
object.aliases
end

def icon
object.image
end
Expand Down
16 changes: 16 additions & 0 deletions app/services/activitypub/process_status_update_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
include Redisable
include Lockable

class AbortError < ::StandardError; end

def call(status, activity_json, object_json, request_id: nil)
raise ArgumentError, 'Status has unsaved changes' if status.changed?

Expand All @@ -30,6 +32,9 @@ def call(status, activity_json, object_json, request_id: nil)
handle_implicit_update!
end

@status
rescue AbortError
@status.reload
@status
end

Expand All @@ -46,6 +51,7 @@ def handle_explicit_update!
update_poll!
update_immediate_attributes!
update_metadata!
validate_status_mentions!
create_edits!
end

Expand Down Expand Up @@ -158,6 +164,15 @@ def valid_status?
!Admin::NgWord.reject?("#{@status_parser.spoiler_text}\n#{@status_parser.text}") && !Admin::NgWord.hashtag_reject?(@raw_tags.size)
end

def validate_status_mentions!
raise AbortError if mention_to_stranger? && Admin::NgWord.stranger_mention_reject?("#{@status.spoiler_text}\n#{@status.text}")
end

def mention_to_stranger?
@status.mentions.map(&:account).to_a.any? { |mentioned_account| mentioned_account.id != @status.account.id && !mentioned_account.following?(@status.account) } ||
(@status.thread.present? && @status.thread.account.id != @status.account.id && !@status.thread.account.following?(@status.account))
end

def update_immediate_attributes!
@status.text = @status_parser.text || ''
@status.spoiler_text = @status_parser.spoiler_text || ''
Expand Down Expand Up @@ -247,6 +262,7 @@ def update_emojis!
emoji.image_remote_url = custom_emoji_parser.image_remote_url
emoji.license = custom_emoji_parser.license
emoji.is_sensitive = custom_emoji_parser.is_sensitive
emoji.aliases = custom_emoji_parser.aliases
emoji.save
rescue Seahorse::Client::NetworkingError => e
Rails.logger.warn "Error storing emoji: #{e}"
Expand Down
3 changes: 2 additions & 1 deletion app/services/concerns/payloadable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ def serialize_payload(record, serializer, options = {})
always_sign = options.delete(:always_sign)
payload = ActiveModelSerializers::SerializableResource.new(record, options.merge(serializer: serializer, adapter: ActivityPub::Adapter)).as_json
object = record.respond_to?(:virtual_object) ? record.virtual_object : record
bearcap = object.is_a?(String) && record.respond_to?(:type) && (record.type == 'Create' || record.type == 'Update')

if ((object.respond_to?(:sign?) && object.sign?) && signer && (always_sign || signing_enabled?)) || object.is_a?(String)
if ((object.respond_to?(:sign?) && object.sign?) && signer && (always_sign || signing_enabled?)) || bearcap
ActivityPub::LinkedDataSignature.new(payload).sign!(signer, sign_with: sign_with)
else
payload
Expand Down
2 changes: 1 addition & 1 deletion app/services/post_status_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,7 @@ def validate_status_mentions!
end

def mention_to_stranger?
@status.mentions.map(&:account).to_a.any? { |mentioned_account| mentioned_account.id != @account && !mentioned_account.following?(@account) } ||
@status.mentions.map(&:account).to_a.any? { |mentioned_account| mentioned_account.id != @account.id && !mentioned_account.following?(@account) } ||
(@in_reply_to && @in_reply_to.account.id != @account.id && !@in_reply_to.account.following?(@account))
end

Expand Down
15 changes: 13 additions & 2 deletions app/services/update_status_service.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,10 +35,13 @@ def call(status, account_id, options = {})
update_poll! if @options.key?(:poll)
update_immediate_attributes!
create_edit! unless @options[:no_history]

reset_preview_card!
process_mentions_service.call(@status)
validate_status_mentions!
end

queue_poll_notifications!
reset_preview_card!
update_metadata!
update_references!
broadcast_updates!
Expand Down Expand Up @@ -81,6 +84,15 @@ def validate_status!
raise Mastodon::ValidationError, I18n.t('statuses.too_many_hashtags') if Admin::NgWord.hashtag_reject_with_extractor?(@options[:text])
end

def validate_status_mentions!
raise Mastodon::ValidationError, I18n.t('statuses.contains_ng_words') if mention_to_stranger? && Setting.stranger_mention_from_local_ng && Admin::NgWord.stranger_mention_reject?("#{@options[:spoiler_text]}\n#{@options[:text]}")
end

def mention_to_stranger?
@status.mentions.map(&:account).to_a.any? { |mentioned_account| mentioned_account.id != @status.account.id && !mentioned_account.following?(@status.account) } ||
(@status.thread.present? && @status.thread.account.id != @status.account.id && !@status.thread.account.following?(@status.account))
end

def validate_media!
return [] if @options[:media_ids].blank? || !@options[:media_ids].is_a?(Enumerable)

Expand Down Expand Up @@ -167,7 +179,6 @@ def update_references!

def update_metadata!
ProcessHashtagsService.new.call(@status)
process_mentions_service.call(@status)

@status.update(limited_scope: :circle) if process_mentions_service.mentions?
end
Expand Down
44 changes: 44 additions & 0 deletions app/views/admin/domain_blocks/_domain_block_list.html.haml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
%h4= I18n.t('admin.domain_blocks.headers.harassment')

.fields-group
= f.input :reject_favourite, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_favourite'), hint: I18n.t('admin.domain_blocks.reject_favourite_hint')

.fields-group
= f.input :reject_reply, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reply'), hint: I18n.t('admin.domain_blocks.reject_reply_hint')

.fields-group
= f.input :reject_reply_exclude_followers, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reply_exclude_followers'), hint: I18n.t('admin.domain_blocks.reject_reply_exclude_followers_hint')

.fields-group
= f.input :reject_hashtag, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_hashtag'), hint: I18n.t('admin.domain_blocks.reject_hashtag_hint')

.fields-group
= f.input :reject_straight_follow, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_straight_follow'), hint: I18n.t('admin.domain_blocks.reject_straight_follow_hint')

.fields-group
= f.input :reject_new_follow, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_new_follow'), hint: I18n.t('admin.domain_blocks.reject_new_follow_hint')

.fields-group
= f.input :reject_friend, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_friend'), hint: I18n.t('admin.domain_blocks.reject_friend_hint')

%h4= I18n.t('admin.domain_blocks.headers.invalid_privacy')

.fields-group
= f.input :reject_send_not_public_searchability, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_send_not_public_searchability'), hint: I18n.t('admin.domain_blocks.reject_send_not_public_searchability_hint')

.fields-group
= f.input :reject_send_dissubscribable, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_send_dissubscribable'), hint: I18n.t('admin.domain_blocks.reject_send_dissubscribable_hint')

.fields-group
= f.input :detect_invalid_subscription, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.detect_invalid_subscription'), hint: I18n.t('admin.domain_blocks.detect_invalid_subscription_hint')

%h4= I18n.t('admin.domain_blocks.headers.disagreement')

.fields-group
= f.input :reject_send_public_unlisted, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_send_public_unlisted'), hint: I18n.t('admin.domain_blocks.reject_send_public_unlisted_hint')

.fields-group
= f.input :reject_send_media, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_send_media'), hint: I18n.t('admin.domain_blocks.reject_send_media_hint')

.fields-group
= f.input :reject_send_sensitive, as: :boolean, kmyblue: true, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_send_sensitive'), hint: I18n.t('admin.domain_blocks.reject_send_sensitive_hint')
43 changes: 4 additions & 39 deletions app/views/admin/domain_blocks/edit.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -11,47 +11,12 @@
.fields-row__column.fields-row__column-6.fields-group
= f.input :severity, collection: DomainBlock.severities.keys, wrapper: :with_label, include_blank: false, label_method: ->(type) { t("admin.domain_blocks.new.severity.#{type}") }, hint: t('admin.domain_blocks.new.severity.desc_html')

.fields-group
= f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint')

.fields-group
= f.input :reject_favourite, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_favourite'), hint: I18n.t('admin.domain_blocks.reject_favourite_hint')

.fields-group
= f.input :reject_reply, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reply'), hint: I18n.t('admin.domain_blocks.reject_reply_hint')

.fields-group
= f.input :reject_reply_exclude_followers, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reply_exclude_followers'), hint: I18n.t('admin.domain_blocks.reject_reply_exclude_followers_hint')

.fields-group
= f.input :reject_send_not_public_searchability, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_send_not_public_searchability'), hint: I18n.t('admin.domain_blocks.reject_send_not_public_searchability_hint')

.fields-group
= f.input :reject_send_dissubscribable, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_send_dissubscribable'), hint: I18n.t('admin.domain_blocks.reject_send_dissubscribable_hint')

.fields-group
= f.input :reject_send_public_unlisted, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_send_public_unlisted'), hint: I18n.t('admin.domain_blocks.reject_send_public_unlisted_hint')

.fields-group
= f.input :reject_send_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_send_media'), hint: I18n.t('admin.domain_blocks.reject_send_media_hint')
= render 'domain_block_list', f: f

.fields-group
= f.input :reject_send_sensitive, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_send_sensitive'), hint: I18n.t('admin.domain_blocks.reject_send_sensitive_hint')
%h4= I18n.t('admin.domain_blocks.headers.mastodon_default')

.fields-group
= f.input :reject_hashtag, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_hashtag'), hint: I18n.t('admin.domain_blocks.reject_hashtag_hint')

.fields-group
= f.input :reject_straight_follow, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_straight_follow'), hint: I18n.t('admin.domain_blocks.reject_straight_follow_hint')

.fields-group
= f.input :reject_new_follow, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_new_follow'), hint: I18n.t('admin.domain_blocks.reject_new_follow_hint')

.fields-group
= f.input :reject_friend, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_friend'), hint: I18n.t('admin.domain_blocks.reject_friend_hint')

.fields-group
= f.input :detect_invalid_subscription, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.detect_invalid_subscription'), hint: I18n.t('admin.domain_blocks.detect_invalid_subscription_hint')
= f.input :reject_media, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_media'), hint: I18n.t('admin.domain_blocks.reject_media_hint')

.fields-group
= f.input :reject_reports, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.reject_reports'), hint: I18n.t('admin.domain_blocks.reject_reports_hint')
Expand All @@ -69,7 +34,7 @@
= f.input :hidden, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.hidden'), hint: I18n.t('admin.domain_blocks.hidden_hint')

.fields-group
= f.input :hidden_anonymous, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.hidden_anonymous'), hint: I18n.t('admin.domain_blocks.hidden_anonymous_hint')
= f.input :hidden_anonymous, kmyblue: true, as: :boolean, wrapper: :with_label, label: I18n.t('admin.domain_blocks.hidden_anonymous'), hint: I18n.t('admin.domain_blocks.hidden_anonymous_hint')

.actions
= f.button :button, t('generic.save_changes'), type: :submit
Loading

0 comments on commit b992e67

Please sign in to comment.