mirror of
https://github.com/mastodon/mastodon.git
synced 2025-07-12 15:33:14 +00:00
Compare commits
5 Commits
0a604dcea1
...
07c9accec8
Author | SHA1 | Date | |
---|---|---|---|
![]() |
07c9accec8 | ||
![]() |
b23596109f | ||
![]() |
75707d05a9 | ||
![]() |
c1f8763e9b | ||
![]() |
129dd5bd9c |
9
app/controllers/admin/settings/protections_controller.rb
Normal file
9
app/controllers/admin/settings/protections_controller.rb
Normal file
|
@ -0,0 +1,9 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class Admin::Settings::ProtectionsController < Admin::SettingsController
|
||||
private
|
||||
|
||||
def after_update_redirect_path
|
||||
admin_settings_protections_path
|
||||
end
|
||||
end
|
1
app/javascript/material-icons/400-24px/security.svg
Normal file
1
app/javascript/material-icons/400-24px/security.svg
Normal file
|
@ -0,0 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24"><path d="M480-80q-139-35-229.5-159.5T160-516v-244l320-120 320 120v244q0 152-90.5 276.5T480-80Zm0-84q97-30 162-118.5T718-480H480v-315l-240 90v207q0 7 2 18h238v316Z"/></svg>
|
After Width: | Height: | Size: 259 B |
|
@ -14,7 +14,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||
private
|
||||
|
||||
def create_status
|
||||
return reject_payload! if unsupported_object_type? || non_matching_uri_hosts?(@account.uri, object_uri) || tombstone_exists? || !related_to_local_activity?
|
||||
return reject_payload! if unsupported_object_type? || non_matching_uri_hosts?(@account.uri, object_uri) || tombstone_exists? || !related_to_local_activity? || reject_pattern?
|
||||
|
||||
with_redis_lock("create:#{object_uri}") do
|
||||
return if delete_arrived_first?(object_uri) || poll_vote?
|
||||
|
@ -450,6 +450,10 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
|||
Tombstone.exists?(uri: object_uri)
|
||||
end
|
||||
|
||||
def reject_pattern?
|
||||
Setting.reject_pattern.present? && @object['content']&.match?(Setting.reject_pattern)
|
||||
end
|
||||
|
||||
def forward_for_reply
|
||||
return unless @status.distributable? && @json['signature'].present? && reply_to_local?
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ class Form::AdminSettings
|
|||
app_icon
|
||||
favicon
|
||||
min_age
|
||||
reject_pattern
|
||||
).freeze
|
||||
|
||||
INTEGER_KEYS = %i(
|
||||
|
@ -94,6 +95,7 @@ class Form::AdminSettings
|
|||
validates :media_cache_retention_period, :content_cache_retention_period, :backups_retention_period, numericality: { only_integer: true }, allow_blank: true, if: -> { defined?(@media_cache_retention_period) || defined?(@content_cache_retention_period) || defined?(@backups_retention_period) }
|
||||
validates :min_age, numericality: { only_integer: true }, allow_blank: true, if: -> { defined?(@min_age) }
|
||||
validates :site_short_description, length: { maximum: DESCRIPTION_LIMIT }, if: -> { defined?(@site_short_description) }
|
||||
validates :reject_pattern, regexp_syntax: true, if: -> { defined?(@reject_pattern) }
|
||||
validates :status_page_url, url: true, allow_blank: true
|
||||
validate :validate_site_uploads
|
||||
|
||||
|
|
11
app/validators/regexp_syntax_validator.rb
Normal file
11
app/validators/regexp_syntax_validator.rb
Normal file
|
@ -0,0 +1,11 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
class RegexpSyntaxValidator < ActiveModel::EachValidator
|
||||
def validate_each(record, attribute, value)
|
||||
return if value.blank?
|
||||
|
||||
Regexp.compile(value)
|
||||
rescue RegexpError => e
|
||||
record.errors.add(attribute, I18n.t('applications.invalid_regexp', message: e.message))
|
||||
end
|
||||
end
|
22
app/views/admin/settings/protections/show.html.haml
Normal file
22
app/views/admin/settings/protections/show.html.haml
Normal file
|
@ -0,0 +1,22 @@
|
|||
- content_for :page_title do
|
||||
= t('admin.settings.protections.title')
|
||||
|
||||
- content_for :heading do
|
||||
%h2= t('admin.settings.title')
|
||||
= render partial: 'admin/settings/shared/links'
|
||||
|
||||
= simple_form_for @admin_settings, url: admin_settings_protections_path, html: { method: :patch } do |f|
|
||||
= render 'shared/error_messages', object: @admin_settings
|
||||
|
||||
%p.lead= t('admin.settings.protections.preamble')
|
||||
|
||||
%h4= t('admin.settings.protections.activitypub')
|
||||
|
||||
.fields-group
|
||||
= f.input :reject_pattern,
|
||||
as: :text,
|
||||
input_html: { rows: 8 },
|
||||
wrapper: :with_block_label
|
||||
|
||||
.actions
|
||||
= f.button :button, t('generic.save_changes'), type: :submit
|
|
@ -7,3 +7,4 @@
|
|||
primary.item :discovery, safe_join([material_symbol('search'), t('admin.settings.discovery.title')]), admin_settings_discovery_path
|
||||
primary.item :content_retention, safe_join([material_symbol('history'), t('admin.settings.content_retention.title')]), admin_settings_content_retention_path
|
||||
primary.item :appearance, safe_join([material_symbol('computer'), t('admin.settings.appearance.title')]), admin_settings_appearance_path
|
||||
primary.item :protections, safe_join([material_symbol('security'), t('admin.settings.protections.title')]), admin_settings_protections_path
|
||||
|
|
|
@ -842,6 +842,10 @@ en:
|
|||
all: To everyone
|
||||
disabled: To no one
|
||||
users: To logged-in local users
|
||||
protections:
|
||||
activitypub: ActivityPub
|
||||
preamble: Settings to protect your server
|
||||
title: Protection
|
||||
registrations:
|
||||
moderation_recommandation: Please make sure you have an adequate and reactive moderation team before you open registrations to everyone!
|
||||
preamble: Control who can create an account on your server.
|
||||
|
@ -1178,6 +1182,7 @@ en:
|
|||
applications:
|
||||
created: Application successfully created
|
||||
destroyed: Application successfully deleted
|
||||
invalid_regexp: 'The provided Regexp is invalid: %{message}'
|
||||
logout: Logout
|
||||
regenerate_token: Regenerate access token
|
||||
token_regenerated: Access token successfully regenerated
|
||||
|
|
|
@ -94,6 +94,7 @@ en:
|
|||
min_age: Users will be asked to confirm their date of birth during sign-up
|
||||
peers_api_enabled: A list of domain names this server has encountered in the fediverse. No data is included here about whether you federate with a given server, just that your server knows about it. This is used by services that collect statistics on federation in a general sense.
|
||||
profile_directory: The profile directory lists all users who have opted-in to be discoverable.
|
||||
reject_pattern: Set a regular expression pattern to inspect Create Activity content, and refuse Activity if you match
|
||||
require_invite_text: When sign-ups require manual approval, make the “Why do you want to join?” text input mandatory rather than optional
|
||||
site_contact_email: How people can reach you for legal or support inquiries.
|
||||
site_contact_username: How people can reach you on Mastodon.
|
||||
|
@ -285,6 +286,7 @@ en:
|
|||
peers_api_enabled: Publish list of discovered servers in the API
|
||||
profile_directory: Enable profile directory
|
||||
registrations_mode: Who can sign-up
|
||||
reject_pattern: Reject Pattern
|
||||
require_invite_text: Require a reason to join
|
||||
show_domain_blocks: Show domain blocks
|
||||
show_domain_blocks_rationale: Show why domains were blocked
|
||||
|
|
|
@ -68,6 +68,7 @@ namespace :admin do
|
|||
resource :about, only: [:show, :update], controller: 'about'
|
||||
resource :appearance, only: [:show, :update], controller: 'appearance'
|
||||
resource :discovery, only: [:show, :update], controller: 'discovery'
|
||||
resource :protections, only: [:show, :update], controller: 'protections'
|
||||
end
|
||||
|
||||
resources :site_uploads, only: [:destroy]
|
||||
|
|
|
@ -52,6 +52,7 @@ defaults: &defaults
|
|||
backups_retention_period: 7
|
||||
captcha_enabled: false
|
||||
allow_referer_origin: false
|
||||
reject_pattern: ''
|
||||
|
||||
development:
|
||||
<<: *defaults
|
||||
|
|
|
@ -26,6 +26,8 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
before do
|
||||
sender.update(uri: ActivityPub::TagManager.instance.uri_for(sender))
|
||||
|
||||
Setting.reject_pattern = 'spam'
|
||||
|
||||
stub_request(:get, 'http://example.com/attachment.png').to_return(request_fixture('avatar.txt'))
|
||||
stub_request(:get, 'http://example.com/emoji.png').to_return(body: attachment_fixture('emojo.png'))
|
||||
stub_request(:get, 'http://example.com/emojib.png').to_return(body: attachment_fixture('emojo.png'), headers: { 'Content-Type' => 'application/octet-stream' })
|
||||
|
@ -95,6 +97,24 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
}
|
||||
end
|
||||
|
||||
let(:spam_object_json) do
|
||||
{
|
||||
id: [ActivityPub::TagManager.instance.uri_for(sender), 'post3'].join('/'),
|
||||
type: 'Note',
|
||||
to: [
|
||||
'https://www.w3.org/ns/activitystreams#Public',
|
||||
ActivityPub::TagManager.instance.uri_for(follower),
|
||||
],
|
||||
content: '@bob lorem ispam',
|
||||
published: 1.hour.ago.utc.iso8601,
|
||||
updated: 1.hour.ago.utc.iso8601,
|
||||
tag: {
|
||||
type: 'Mention',
|
||||
href: ActivityPub::TagManager.instance.uri_for(follower),
|
||||
},
|
||||
}
|
||||
end
|
||||
|
||||
def activity_for_object(json)
|
||||
{
|
||||
'@context': 'https://www.w3.org/ns/activitystreams',
|
||||
|
@ -168,6 +188,17 @@ RSpec.describe ActivityPub::Activity::Create do
|
|||
# It has queued a mention resolve job
|
||||
expect(MentionResolveWorker).to have_enqueued_sidekiq_job(status.id, invalid_mention_json[:tag][:href], anything)
|
||||
end
|
||||
|
||||
it 'do not process posts that contain reject patterns', :aggregate_failures do
|
||||
stub_request(:get, spam_object_json[:id]).to_return(status: 500)
|
||||
described_class.new(activity_for_object(spam_object_json), sender, delivery: true).perform
|
||||
|
||||
# …it creates a status
|
||||
status = Status.find_by(uri: spam_object_json[:id])
|
||||
|
||||
# Check the process did crash
|
||||
expect(status.nil?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#perform' do
|
||||
|
|
25
spec/system/admin/settings/protection_spec.rb
Normal file
25
spec/system/admin/settings/protection_spec.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe 'Admin::Settings::Protections' do
|
||||
let(:admin_user) { Fabricate(:admin_user) }
|
||||
|
||||
before { sign_in(admin_user) }
|
||||
|
||||
it 'Saves changes to protections settings' do
|
||||
visit admin_settings_protections_path
|
||||
|
||||
fill_in reject_pattern_field,
|
||||
with: 'https://foo.bar'
|
||||
|
||||
click_on submit_button
|
||||
|
||||
expect(page)
|
||||
.to have_content(success_message)
|
||||
end
|
||||
|
||||
def reject_pattern_field
|
||||
form_label 'form_admin_settings.reject_pattern'
|
||||
end
|
||||
end
|
55
spec/validators/regexp_syntax_validator_spec.rb
Normal file
55
spec/validators/regexp_syntax_validator_spec.rb
Normal file
|
@ -0,0 +1,55 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe RegexpSyntaxValidator do
|
||||
let(:record_class) do
|
||||
Class.new do
|
||||
include ActiveModel::Validations
|
||||
|
||||
attr_accessor :text
|
||||
|
||||
validates :text, regexp_syntax: true
|
||||
end
|
||||
end
|
||||
|
||||
let(:record) { record_class.new }
|
||||
|
||||
describe '#validate_each' do
|
||||
context 'with a nil value' do
|
||||
it 'does not add errors' do
|
||||
record.text = nil
|
||||
|
||||
expect(record).to be_valid
|
||||
expect(record.errors).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'with a blank' do
|
||||
it 'does not add errors' do
|
||||
record.text = ''
|
||||
|
||||
expect(record).to be_valid
|
||||
expect(record.errors).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'with valid regexp' do
|
||||
it 'does not add errors' do
|
||||
record.text = 'spam|https://foo.bar'
|
||||
|
||||
expect(record).to be_valid
|
||||
expect(record.errors).to be_empty
|
||||
end
|
||||
end
|
||||
|
||||
context 'with more lines than limit' do
|
||||
it 'adds an error' do
|
||||
record.text = '('
|
||||
|
||||
expect(record).to_not be_valid
|
||||
expect(record.errors.where(:text)).to_not be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue
Block a user