Merge commit '2c0441acd7f943a9873b650cf75d33c73d545acf' into glitch-soc/merge-upstream

main-rebase-security-fix
Claire 2024-03-13 19:16:47 +01:00
commit 06881a8669
96 changed files with 232 additions and 277 deletions

View File

@ -73,6 +73,14 @@ class Api::BaseController < ApplicationController
protected
def pagination_max_id
pagination_collection.last.id
end
def pagination_since_id
pagination_collection.first.id
end
def set_pagination_headers(next_path = nil, prev_path = nil)
links = []
links << [next_path, [%w(rel next)]] if next_path

View File

@ -51,11 +51,7 @@ class Api::V1::Accounts::StatusesController < Api::BaseController
@statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
end
def pagination_max_id
@statuses.last.id
end
def pagination_since_id
@statuses.first.id
def pagination_collection
@statuses
end
end

View File

@ -137,12 +137,8 @@ class Api::V1::Admin::AccountsController < Api::BaseController
api_v1_admin_accounts_url(pagination_params(min_id: pagination_since_id)) unless @accounts.empty?
end
def pagination_max_id
@accounts.last.id
end
def pagination_since_id
@accounts.first.id
def pagination_collection
@accounts
end
def records_continue?

View File

@ -77,12 +77,8 @@ class Api::V1::Admin::CanonicalEmailBlocksController < Api::BaseController
api_v1_admin_canonical_email_blocks_url(pagination_params(min_id: pagination_since_id)) unless @canonical_email_blocks.empty?
end
def pagination_max_id
@canonical_email_blocks.last.id
end
def pagination_since_id
@canonical_email_blocks.first.id
def pagination_collection
@canonical_email_blocks
end
def records_continue?

View File

@ -73,12 +73,8 @@ class Api::V1::Admin::DomainAllowsController < Api::BaseController
api_v1_admin_domain_allows_url(pagination_params(min_id: pagination_since_id)) unless @domain_allows.empty?
end
def pagination_max_id
@domain_allows.last.id
end
def pagination_since_id
@domain_allows.first.id
def pagination_collection
@domain_allows
end
def records_continue?

View File

@ -84,12 +84,8 @@ class Api::V1::Admin::DomainBlocksController < Api::BaseController
api_v1_admin_domain_blocks_url(pagination_params(min_id: pagination_since_id)) unless @domain_blocks.empty?
end
def pagination_max_id
@domain_blocks.last.id
end
def pagination_since_id
@domain_blocks.first.id
def pagination_collection
@domain_blocks
end
def records_continue?

View File

@ -70,12 +70,8 @@ class Api::V1::Admin::EmailDomainBlocksController < Api::BaseController
api_v1_admin_email_domain_blocks_url(pagination_params(min_id: pagination_since_id)) unless @email_domain_blocks.empty?
end
def pagination_max_id
@email_domain_blocks.last.id
end
def pagination_since_id
@email_domain_blocks.first.id
def pagination_collection
@email_domain_blocks
end
def records_continue?

View File

@ -75,12 +75,8 @@ class Api::V1::Admin::IpBlocksController < Api::BaseController
api_v1_admin_ip_blocks_url(pagination_params(min_id: pagination_since_id)) unless @ip_blocks.empty?
end
def pagination_max_id
@ip_blocks.last.id
end
def pagination_since_id
@ip_blocks.first.id
def pagination_collection
@ip_blocks
end
def records_continue?

View File

@ -101,12 +101,8 @@ class Api::V1::Admin::ReportsController < Api::BaseController
api_v1_admin_reports_url(pagination_params(min_id: pagination_since_id)) unless @reports.empty?
end
def pagination_max_id
@reports.last.id
end
def pagination_since_id
@reports.first.id
def pagination_collection
@reports
end
def records_continue?

View File

@ -56,12 +56,8 @@ class Api::V1::Admin::TagsController < Api::BaseController
api_v1_admin_tags_url(pagination_params(min_id: pagination_since_id)) unless @tags.empty?
end
def pagination_max_id
@tags.last.id
end
def pagination_since_id
@tags.first.id
def pagination_collection
@tags
end
def records_continue?

View File

@ -54,12 +54,8 @@ class Api::V1::Admin::Trends::Links::PreviewCardProvidersController < Api::BaseC
api_v1_admin_trends_links_preview_card_providers_url(pagination_params(min_id: pagination_since_id)) unless @providers.empty?
end
def pagination_max_id
@providers.last.id
end
def pagination_since_id
@providers.first.id
def pagination_collection
@providers
end
def records_continue?

View File

@ -40,12 +40,8 @@ class Api::V1::BlocksController < Api::BaseController
api_v1_blocks_url pagination_params(since_id: pagination_since_id) unless paginated_blocks.empty?
end
def pagination_max_id
paginated_blocks.last.id
end
def pagination_since_id
paginated_blocks.first.id
def pagination_collection
paginated_blocks
end
def records_continue?

View File

@ -43,12 +43,8 @@ class Api::V1::BookmarksController < Api::BaseController
api_v1_bookmarks_url pagination_params(min_id: pagination_since_id) unless results.empty?
end
def pagination_max_id
results.last.id
end
def pagination_since_id
results.first.id
def pagination_collection
results
end
def records_continue?

View File

@ -41,12 +41,8 @@ class Api::V1::Crypto::EncryptedMessagesController < Api::BaseController
api_v1_crypto_encrypted_messages_url pagination_params(min_id: pagination_since_id) unless @encrypted_messages.empty?
end
def pagination_max_id
@encrypted_messages.last.id
end
def pagination_since_id
@encrypted_messages.first.id
def pagination_collection
@encrypted_messages
end
def records_continue?

View File

@ -50,12 +50,8 @@ class Api::V1::DomainBlocksController < Api::BaseController
api_v1_domain_blocks_url pagination_params(since_id: pagination_since_id) unless @blocks.empty?
end
def pagination_max_id
@blocks.last.id
end
def pagination_since_id
@blocks.first.id
def pagination_collection
@blocks
end
def records_continue?

View File

@ -44,12 +44,8 @@ class Api::V1::EndorsementsController < Api::BaseController
api_v1_endorsements_url pagination_params(since_id: pagination_since_id) unless @accounts.empty?
end
def pagination_max_id
@accounts.last.id
end
def pagination_since_id
@accounts.first.id
def pagination_collection
@accounts
end
def records_continue?

View File

@ -43,12 +43,8 @@ class Api::V1::FavouritesController < Api::BaseController
api_v1_favourites_url pagination_params(min_id: pagination_since_id) unless results.empty?
end
def pagination_max_id
results.last.id
end
def pagination_since_id
results.first.id
def pagination_collection
results
end
def records_continue?

View File

@ -34,12 +34,8 @@ class Api::V1::FollowedTagsController < Api::BaseController
api_v1_followed_tags_url pagination_params(since_id: pagination_since_id) unless @results.empty?
end
def pagination_max_id
@results.last.id
end
def pagination_since_id
@results.first.id
def pagination_collection
@results
end
def records_continue?

View File

@ -71,12 +71,8 @@ class Api::V1::Lists::AccountsController < Api::BaseController
api_v1_list_accounts_url pagination_params(since_id: pagination_since_id) unless @accounts.empty?
end
def pagination_max_id
@accounts.last.id
end
def pagination_since_id
@accounts.first.id
def pagination_collection
@accounts
end
def records_continue?

View File

@ -40,12 +40,8 @@ class Api::V1::MutesController < Api::BaseController
api_v1_mutes_url pagination_params(since_id: pagination_since_id) unless paginated_mutes.empty?
end
def pagination_max_id
paginated_mutes.last.id
end
def pagination_since_id
paginated_mutes.first.id
def pagination_collection
paginated_mutes
end
def records_continue?

View File

@ -79,12 +79,8 @@ class Api::V1::NotificationsController < Api::BaseController
api_v1_notifications_url pagination_params(min_id: pagination_since_id) unless @notifications.empty?
end
def pagination_max_id
@notifications.last.id
end
def pagination_since_id
@notifications.first.id
def pagination_collection
@notifications
end
def browserable_params

View File

@ -63,11 +63,7 @@ class Api::V1::ScheduledStatusesController < Api::BaseController
@statuses.size == limit_param(DEFAULT_STATUSES_LIMIT)
end
def pagination_max_id
@statuses.last.id
end
def pagination_since_id
@statuses.first.id
def pagination_collection
@statuses
end
end

View File

@ -9,12 +9,8 @@ class Api::V1::Timelines::BaseController < Api::BaseController
set_pagination_headers(next_path, prev_path)
end
def pagination_max_id
@statuses.last.id
end
def pagination_since_id
@statuses.first.id
def pagination_collection
@statuses
end
def next_path_params

View File

@ -1,5 +1,3 @@
import Rails from '@rails/ujs';
export const logOut = () => {
const form = document.createElement('form');
@ -9,13 +7,18 @@ export const logOut = () => {
methodInput.setAttribute('type', 'hidden');
form.appendChild(methodInput);
const csrfToken = Rails.csrfToken();
const csrfParam = Rails.csrfParam();
const csrfToken = document.querySelector<HTMLMetaElement>(
'meta[name=csrf-token]',
);
const csrfParam = document.querySelector<HTMLMetaElement>(
'meta[name=csrf-param]',
);
if (csrfParam && csrfToken) {
const csrfInput = document.createElement('input');
csrfInput.setAttribute('name', csrfParam);
csrfInput.setAttribute('value', csrfToken);
csrfInput.setAttribute('name', csrfParam.content);
csrfInput.setAttribute('value', csrfToken.content);
csrfInput.setAttribute('type', 'hidden');
form.appendChild(csrfInput);
}

View File

@ -37,11 +37,11 @@ class Admin::Metrics::Dimension::InstanceLanguagesDimension < Admin::Metrics::Di
end
def earliest_status_id
Mastodon::Snowflake.id_at(@start_at, with_random: false)
Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
end
def latest_status_id
Mastodon::Snowflake.id_at(@end_at, with_random: false)
Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
end
def params

View File

@ -30,10 +30,10 @@ class Admin::Metrics::Dimension::ServersDimension < Admin::Metrics::Dimension::B
end
def earliest_status_id
Mastodon::Snowflake.id_at(@start_at)
Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
end
def latest_status_id
Mastodon::Snowflake.id_at(@end_at)
Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
end
end

View File

@ -40,11 +40,11 @@ class Admin::Metrics::Dimension::TagLanguagesDimension < Admin::Metrics::Dimensi
end
def earliest_status_id
Mastodon::Snowflake.id_at(@start_at, with_random: false)
Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
end
def latest_status_id
Mastodon::Snowflake.id_at(@end_at, with_random: false)
Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
end
def params

View File

@ -40,11 +40,11 @@ class Admin::Metrics::Dimension::TagServersDimension < Admin::Metrics::Dimension
end
def earliest_status_id
Mastodon::Snowflake.id_at(@start_at, with_random: false)
Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
end
def latest_status_id
Mastodon::Snowflake.id_at(@end_at, with_random: false)
Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
end
def params

View File

@ -51,11 +51,11 @@ class Admin::Metrics::Measure::InstanceStatusesMeasure < Admin::Metrics::Measure
end
def earliest_status_id
Mastodon::Snowflake.id_at(@start_at, with_random: false)
Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
end
def latest_status_id
Mastodon::Snowflake.id_at(@end_at, with_random: false)
Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
end
def time_period

View File

@ -50,6 +50,7 @@ class TextFormatter
class << self
include ERB::Util
include ActionView::Helpers::TagHelper
def shortened_link(url, rel_me: false)
url = Addressable::URI.parse(url).to_s
@ -60,9 +61,11 @@ class TextFormatter
suffix = url[prefix.length + 30..]
cutoff = url[prefix.length..].length > 30
<<~HTML.squish.html_safe # rubocop:disable Rails/OutputSafety
<a href="#{h(url)}" target="_blank" rel="#{rel.join(' ')}" translate="no"><span class="invisible">#{h(prefix)}</span><span class="#{cutoff ? 'ellipsis' : ''}">#{h(display_url)}</span><span class="invisible">#{h(suffix)}</span></a>
HTML
tag.a href: url, target: '_blank', rel: rel.join(' '), translate: 'no' do
tag.span(prefix, class: 'invisible') +
tag.span(display_url, class: (cutoff ? 'ellipsis' : '')) +
tag.span(suffix, class: 'invisible')
end
rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
h(url)
end

View File

@ -41,7 +41,7 @@ class CustomFilter < ApplicationRecord
validates :title, :context, presence: true
validate :context_must_be_valid
before_validation :clean_up_contexts
normalizes :context, with: ->(context) { context.map(&:strip).filter_map(&:presence) }
before_save :prepare_cache_invalidation!
before_destroy :prepare_cache_invalidation!
@ -114,10 +114,6 @@ class CustomFilter < ApplicationRecord
private
def clean_up_contexts
self.context = Array(context).map(&:strip).filter_map(&:presence)
end
def context_must_be_valid
errors.add(:context, I18n.t('filters.errors.invalid_context')) if invalid_context_value?
end

View File

@ -50,7 +50,7 @@ RSpec.describe ActivityPub::Activity::Add do
end
it 'fetches the status and pins it' do
allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil, request_id: nil| # rubocop:disable Lint/UnusedBlockArgument
allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil, **|
expect(uri).to eq 'https://example.com/unknown'
expect(id).to be true
expect(on_behalf_of&.following?(sender)).to be true
@ -64,7 +64,7 @@ RSpec.describe ActivityPub::Activity::Add do
context 'when there is no local follower' do
it 'tries to fetch the status' do
allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil, request_id: nil| # rubocop:disable Lint/UnusedBlockArgument
allow(service_stub).to receive(:call) do |uri, id: true, on_behalf_of: nil, **|
expect(uri).to eq 'https://example.com/unknown'
expect(id).to be true
expect(on_behalf_of).to be_nil

View File

@ -32,4 +32,12 @@ RSpec.describe CustomFilter do
expect(record).to model_have_error_on_field(:context)
end
end
describe 'Normalizations' do
it 'cleans up context values' do
record = described_class.new(context: ['home', 'notifications', 'public ', ''])
expect(record.context).to eq(%w(home notifications public))
end
end
end

View File

@ -21,6 +21,7 @@ require 'paperclip/matchers'
require 'capybara/rspec'
require 'chewy/rspec'
require 'email_spec/rspec'
require 'test_prof/recipes/rspec/before_all'
Dir[Rails.root.join('spec', 'support', '**', '*.rb')].each { |f| require f }

View File

@ -2,25 +2,20 @@
require 'rails_helper'
RSpec.describe Api::V2::Filters::KeywordsController do
render_views
RSpec.describe 'API V2 Filters Keywords' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:filter) { Fabricate(:custom_filter, account: user.account) }
let(:other_user) { Fabricate(:user) }
let(:other_filter) { Fabricate(:custom_filter, account: other_user.account) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
describe 'GET /api/v2/filters/:filter_id/keywords' do
let(:scopes) { 'read:filters' }
let!(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
it 'returns http success' do
get :index, params: { filter_id: filter.id }
get "/api/v2/filters/#{filter.id}/keywords", headers: headers
expect(response).to have_http_status(200)
expect(body_as_json)
.to contain_exactly(
@ -30,18 +25,18 @@ RSpec.describe Api::V2::Filters::KeywordsController do
context "when trying to access another's user filters" do
it 'returns http not found' do
get :index, params: { filter_id: other_filter.id }
get "/api/v2/filters/#{other_filter.id}/keywords", headers: headers
expect(response).to have_http_status(404)
end
end
end
describe 'POST #create' do
describe 'POST /api/v2/filters/:filter_id/keywords' do
let(:scopes) { 'write:filters' }
let(:filter_id) { filter.id }
before do
post :create, params: { filter_id: filter_id, keyword: 'magic', whole_word: false }
post "/api/v2/filters/#{filter_id}/keywords", headers: headers, params: { keyword: 'magic', whole_word: false }
end
it 'creates a filter', :aggregate_failures do
@ -65,12 +60,12 @@ RSpec.describe Api::V2::Filters::KeywordsController do
end
end
describe 'GET #show' do
describe 'GET /api/v2/filters/keywords/:id' do
let(:scopes) { 'read:filters' }
let(:keyword) { Fabricate(:custom_filter_keyword, keyword: 'foo', whole_word: false, custom_filter: filter) }
before do
get :show, params: { id: keyword.id }
get "/api/v2/filters/keywords/#{keyword.id}", headers: headers
end
it 'responds with the keyword', :aggregate_failures do
@ -90,12 +85,12 @@ RSpec.describe Api::V2::Filters::KeywordsController do
end
end
describe 'PUT #update' do
describe 'PUT /api/v2/filters/keywords/:id' do
let(:scopes) { 'write:filters' }
let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
before do
get :update, params: { id: keyword.id, keyword: 'updated' }
put "/api/v2/filters/keywords/#{keyword.id}", headers: headers, params: { keyword: 'updated' }
end
it 'updates the keyword', :aggregate_failures do
@ -113,12 +108,12 @@ RSpec.describe Api::V2::Filters::KeywordsController do
end
end
describe 'DELETE #destroy' do
describe 'DELETE /api/v2/filters/keywords/:id' do
let(:scopes) { 'write:filters' }
let(:keyword) { Fabricate(:custom_filter_keyword, custom_filter: filter) }
before do
delete :destroy, params: { id: keyword.id }
delete "/api/v2/filters/keywords/#{keyword.id}", headers: headers
end
it 'destroys the keyword', :aggregate_failures do

View File

@ -2,25 +2,20 @@
require 'rails_helper'
RSpec.describe Api::V2::Filters::StatusesController do
render_views
RSpec.describe 'API V2 Filters Statuses' do
let(:user) { Fabricate(:user) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:filter) { Fabricate(:custom_filter, account: user.account) }
let(:other_user) { Fabricate(:user) }
let(:other_filter) { Fabricate(:custom_filter, account: other_user.account) }
let(:headers) { { 'Authorization' => "Bearer #{token.token}" } }
before do
allow(controller).to receive(:doorkeeper_token) { token }
end
describe 'GET #index' do
describe 'GET /api/v2/filters/:filter_id/statuses' do
let(:scopes) { 'read:filters' }
let!(:status_filter) { Fabricate(:custom_filter_status, custom_filter: filter) }
it 'returns http success' do
get :index, params: { filter_id: filter.id }
get "/api/v2/filters/#{filter.id}/statuses", headers: headers
expect(response).to have_http_status(200)
expect(body_as_json)
.to contain_exactly(
@ -30,7 +25,7 @@ RSpec.describe Api::V2::Filters::StatusesController do
context "when trying to access another's user filters" do
it 'returns http not found' do
get :index, params: { filter_id: other_filter.id }
get "/api/v2/filters/#{other_filter.id}/statuses", headers: headers
expect(response).to have_http_status(404)
end
end
@ -42,7 +37,7 @@ RSpec.describe Api::V2::Filters::StatusesController do
let!(:status) { Fabricate(:status) }
before do
post :create, params: { filter_id: filter_id, status_id: status.id }
post "/api/v2/filters/#{filter_id}/statuses", headers: headers, params: { status_id: status.id }
end
it 'creates a filter', :aggregate_failures do
@ -65,12 +60,12 @@ RSpec.describe Api::V2::Filters::StatusesController do
end
end
describe 'GET #show' do
describe 'GET /api/v2/filters/statuses/:id' do
let(:scopes) { 'read:filters' }
let!(:status_filter) { Fabricate(:custom_filter_status, custom_filter: filter) }
before do
get :show, params: { id: status_filter.id }
get "/api/v2/filters/statuses/#{status_filter.id}", headers: headers
end
it 'responds with the filter', :aggregate_failures do
@ -89,12 +84,12 @@ RSpec.describe Api::V2::Filters::StatusesController do
end
end
describe 'DELETE #destroy' do
describe 'DELETE /api/v2/filters/statuses/:id' do
let(:scopes) { 'write:filters' }
let(:status_filter) { Fabricate(:custom_filter_status, custom_filter: filter) }
before do
delete :destroy, params: { id: status_filter.id }
delete "/api/v2/filters/statuses/#{status_filter.id}", headers: headers
end
it 'destroys the filter', :aggregate_failures do

View File

@ -39,7 +39,7 @@ module TestEndpoints
/api/v1/accounts/lookup?acct=alice
/api/v1/statuses/110224538612341312
/api/v1/statuses/110224538612341312/context
/api/v1/polls/12345
/api/v1/polls/123456789
/api/v1/trends/statuses
/api/v1/directory
).freeze
@ -166,14 +166,18 @@ describe 'Caching behavior' do
ActionController::Base.allow_forgery_protection = old
end
let(:alice) { Fabricate(:account, username: 'alice') }
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Moderator')) }
let(:alice) { Account.find_by(username: 'alice') }
let(:user) { User.find_by(email: 'user@host.example') }
let(:token) { Doorkeeper::AccessToken.find_by(resource_owner_id: user.id) }
before do
status = Fabricate(:status, account: alice, id: '110224538612341312')
Fabricate(:status, account: alice, id: '110224538643211312', visibility: :private)
before_all do
alice = Fabricate(:account, username: 'alice')
user = Fabricate(:user, email: 'user@host.example', role: UserRole.find_by(name: 'Moderator'))
status = Fabricate(:status, account: alice, id: 110_224_538_612_341_312)
Fabricate(:status, account: alice, id: 110_224_538_643_211_312, visibility: :private)
Fabricate(:invite, code: 'abcdef')
Fabricate(:poll, status: status, account: alice, id: '12345')
Fabricate(:poll, status: status, account: alice, id: 123_456_789)
Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read')
user.account.follow!(alice)
end
@ -321,8 +325,6 @@ describe 'Caching behavior' do
end
context 'with an auth token' do
let!(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
TestEndpoints::ALWAYS_CACHED.each do |endpoint|
describe endpoint do
before do
@ -585,8 +587,6 @@ describe 'Caching behavior' do
end
context 'with an auth token' do
let!(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'read') }
TestEndpoints::ALWAYS_CACHED.each do |endpoint|
describe endpoint do
before do

View File

@ -3,25 +3,38 @@
require 'rails_helper'
describe 'Content-Security-Policy' do
it 'sets the expected CSP headers' do
allow(SecureRandom).to receive(:base64).with(16).and_return('ZbA+JmE7+bK8F5qvADZHuQ==')
before { allow(SecureRandom).to receive(:base64).with(16).and_return('ZbA+JmE7+bK8F5qvADZHuQ==') }
it 'sets the expected CSP headers' do
get '/'
expect(response.headers['Content-Security-Policy'].split(';').map(&:strip)).to contain_exactly(
"base-uri 'none'",
"default-src 'none'",
"frame-ancestors 'none'",
"font-src 'self' https://cb6e6126.ngrok.io",
"img-src 'self' data: blob: https://cb6e6126.ngrok.io",
"style-src 'self' https://cb6e6126.ngrok.io 'nonce-ZbA+JmE7+bK8F5qvADZHuQ=='",
"media-src 'self' data: https://cb6e6126.ngrok.io",
"frame-src 'self' https:",
"manifest-src 'self' https://cb6e6126.ngrok.io",
"form-action 'self'",
"child-src 'self' blob: https://cb6e6126.ngrok.io",
"worker-src 'self' blob: https://cb6e6126.ngrok.io",
"connect-src 'self' data: blob: https://cb6e6126.ngrok.io ws://cb6e6126.ngrok.io:4000",
"script-src 'self' https://cb6e6126.ngrok.io 'wasm-unsafe-eval'"
)
expect(response_csp_headers)
.to match_array(expected_csp_headers)
end
def response_csp_headers
response
.headers['Content-Security-Policy']
.split(';')
.map(&:strip)
end
def expected_csp_headers
<<~CSP.split("\n").map(&:strip)
base-uri 'none'
child-src 'self' blob: https://cb6e6126.ngrok.io
connect-src 'self' data: blob: https://cb6e6126.ngrok.io ws://cb6e6126.ngrok.io:4000
default-src 'none'
font-src 'self' https://cb6e6126.ngrok.io
form-action 'self'
frame-ancestors 'none'
frame-src 'self' https:
img-src 'self' data: blob: https://cb6e6126.ngrok.io
manifest-src 'self' https://cb6e6126.ngrok.io
media-src 'self' data: https://cb6e6126.ngrok.io
script-src 'self' https://cb6e6126.ngrok.io 'wasm-unsafe-eval'
style-src 'self' https://cb6e6126.ngrok.io 'nonce-ZbA+JmE7+bK8F5qvADZHuQ=='
worker-src 'self' blob: https://cb6e6126.ngrok.io
CSP
end
end

View File

@ -2,7 +2,7 @@
require 'rails_helper'
describe AccountSearchService, type: :service do
describe AccountSearchService do
describe '#call' do
context 'with a query to ignore' do
it 'returns empty array for missing query' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
describe AccountStatusesCleanupService, type: :service do
describe AccountStatusesCleanupService do
let(:account) { Fabricate(:account, username: 'alice', domain: nil) }
let(:account_policy) { Fabricate(:account_statuses_cleanup_policy, account: account) }
let!(:unrelated_status) { Fabricate(:status, created_at: 3.years.ago) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::FetchFeaturedCollectionService, type: :service do
RSpec.describe ActivityPub::FetchFeaturedCollectionService do
subject { described_class.new }
let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'https://example.com/account', featured_collection_url: 'https://example.com/account/pinned') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService, type: :service do
RSpec.describe ActivityPub::FetchFeaturedTagsCollectionService do
subject { described_class.new }
let(:collection_url) { 'https://example.com/account/tags' }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::FetchRemoteAccountService, type: :service do
RSpec.describe ActivityPub::FetchRemoteAccountService do
subject { described_class.new }
let!(:actor) do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::FetchRemoteActorService, type: :service do
RSpec.describe ActivityPub::FetchRemoteActorService do
subject { described_class.new }
let!(:actor) do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::FetchRemoteKeyService, type: :service do
RSpec.describe ActivityPub::FetchRemoteKeyService do
subject { described_class.new }
let(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice' }] } }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::FetchRemoteStatusService, type: :service do
RSpec.describe ActivityPub::FetchRemoteStatusService do
include ActionView::Helpers::TextHelper
subject { described_class.new }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::FetchRepliesService, type: :service do
RSpec.describe ActivityPub::FetchRepliesService do
subject { described_class.new }
let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/account') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::ProcessAccountService, type: :service do
RSpec.describe ActivityPub::ProcessAccountService do
subject { described_class.new }
context 'with property values, an avatar, and a profile header' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::ProcessCollectionService, type: :service do
RSpec.describe ActivityPub::ProcessCollectionService do
subject { described_class.new }
let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/account') }

View File

@ -6,7 +6,7 @@ def poll_option_json(name, votes)
{ type: 'Note', name: name, replies: { type: 'Collection', totalItems: votes } }
end
RSpec.describe ActivityPub::ProcessStatusUpdateService, type: :service do
RSpec.describe ActivityPub::ProcessStatusUpdateService do
subject { described_class.new }
let!(:status) { Fabricate(:status, text: 'Hello world', account: Fabricate(:account, domain: 'example.com')) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::SynchronizeFollowersService, type: :service do
RSpec.describe ActivityPub::SynchronizeFollowersService do
subject { described_class.new }
let(:actor) { Fabricate(:account, domain: 'example.com', uri: 'http://example.com/account', inbox_url: 'http://example.com/inbox') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe AfterBlockDomainFromAccountService, type: :service do
RSpec.describe AfterBlockDomainFromAccountService do
subject { described_class.new }
let!(:wolf) { Fabricate(:account, username: 'wolf', domain: 'evil.org', inbox_url: 'https://evil.org/inbox', protocol: :activitypub) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe AfterBlockService, type: :service do
RSpec.describe AfterBlockService do
subject { described_class.new.call(account, target_account) }
let(:account) { Fabricate(:account) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe AppSignUpService, type: :service do
RSpec.describe AppSignUpService do
subject { described_class.new }
let(:app) { Fabricate(:application, scopes: 'read write') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe AuthorizeFollowService, type: :service do
RSpec.describe AuthorizeFollowService do
subject { described_class.new }
let(:sender) { Fabricate(:account, username: 'alice') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe BackupService, type: :service do
RSpec.describe BackupService do
subject(:service_call) { described_class.new.call(backup) }
let!(:user) { Fabricate(:user) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe BatchedRemoveStatusService, :sidekiq_inline, type: :service do
RSpec.describe BatchedRemoveStatusService, :sidekiq_inline do
subject { described_class.new }
let!(:alice) { Fabricate(:account) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe BlockDomainService, type: :service do
RSpec.describe BlockDomainService do
subject { described_class.new }
let!(:bad_account) { Fabricate(:account, username: 'badguy666', domain: 'evil.org') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe BlockService, type: :service do
RSpec.describe BlockService do
subject { described_class.new }
let(:sender) { Fabricate(:account, username: 'alice') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe BootstrapTimelineService, type: :service do
RSpec.describe BootstrapTimelineService do
subject { described_class.new }
context 'when the new user has registered from an invite' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ClearDomainMediaService, type: :service do
RSpec.describe ClearDomainMediaService do
subject { described_class.new }
let!(:bad_account) { Fabricate(:account, username: 'badguy666', domain: 'evil.org') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe DeleteAccountService, type: :service do
RSpec.describe DeleteAccountService do
shared_examples 'common behavior' do
subject { described_class.new.call(account) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe FanOutOnWriteService, type: :service do
RSpec.describe FanOutOnWriteService do
subject { described_class.new }
let(:last_active_at) { Time.now.utc }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe FavouriteService, type: :service do
RSpec.describe FavouriteService do
subject { described_class.new }
let(:sender) { Fabricate(:account, username: 'alice') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe FetchLinkCardService, type: :service do
RSpec.describe FetchLinkCardService do
subject { described_class.new }
let(:html) { '<!doctype html><title>Hello world</title>' }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
describe FetchOEmbedService, type: :service do
describe FetchOEmbedService do
subject { described_class.new }
before do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe FetchRemoteStatusService, type: :service do
RSpec.describe FetchRemoteStatusService do
let(:account) { Fabricate(:account, domain: 'example.org', uri: 'https://example.org/foo') }
let(:prefetched_body) { nil }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe FetchResourceService, type: :service do
RSpec.describe FetchResourceService do
describe '#call' do
subject { described_class.new.call(url) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe FollowService, type: :service do
RSpec.describe FollowService do
subject { described_class.new }
let(:sender) { Fabricate(:account, username: 'alice') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ImportService, :sidekiq_inline, type: :service do
RSpec.describe ImportService, :sidekiq_inline do
include RoutingHelper
let!(:account) { Fabricate(:account, locked: false) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe MuteService, type: :service do
RSpec.describe MuteService do
subject { described_class.new.call(account, target_account) }
let(:account) { Fabricate(:account) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe NotifyService, type: :service do
RSpec.describe NotifyService do
subject { described_class.new.call(recipient, type, activity) }
let(:user) { Fabricate(:user) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe PostStatusService, type: :service do
RSpec.describe PostStatusService do
subject { described_class.new }
it 'creates a new status' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe PrecomputeFeedService, type: :service do
RSpec.describe PrecomputeFeedService do
subject { described_class.new }
describe 'call' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ProcessMentionsService, type: :service do
RSpec.describe ProcessMentionsService do
subject { described_class.new }
let(:account) { Fabricate(:account, username: 'alice') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe PurgeDomainService, type: :service do
RSpec.describe PurgeDomainService do
subject { described_class.new }
let(:domain) { 'obsolete.org' }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ReblogService, type: :service do
RSpec.describe ReblogService do
let(:alice) { Fabricate(:account, username: 'alice') }
context 'when creates a reblog with appropriate visibility' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe RejectFollowService, type: :service do
RSpec.describe RejectFollowService do
subject { described_class.new }
let(:sender) { Fabricate(:account, username: 'alice') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe RemoveFromFollowersService, type: :service do
RSpec.describe RemoveFromFollowersService do
subject { described_class.new }
let(:bob) { Fabricate(:account, username: 'bob') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe RemoveStatusService, :sidekiq_inline, type: :service do
RSpec.describe RemoveStatusService, :sidekiq_inline do
subject { described_class.new }
let!(:alice) { Fabricate(:account) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ReportService, type: :service do
RSpec.describe ReportService do
subject { described_class.new }
let(:source_account) { Fabricate(:account) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe ResolveAccountService, type: :service do
RSpec.describe ResolveAccountService do
subject { described_class.new }
before do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
describe ResolveURLService, type: :service do
describe ResolveURLService do
subject { described_class.new }
describe '#call' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
describe SearchService, type: :service do
describe SearchService do
subject { described_class.new }
describe '#call' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe SoftwareUpdateCheckService, type: :service do
RSpec.describe SoftwareUpdateCheckService do
subject { described_class.new }
shared_examples 'when the feature is enabled' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe SuspendAccountService, :sidekiq_inline, type: :service do
RSpec.describe SuspendAccountService, :sidekiq_inline do
shared_examples 'common behavior' do
subject { described_class.new.call(account) }

View File

@ -0,0 +1,21 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe TagSearchService do
describe '#call' do
let!(:one) { Fabricate(:tag, name: 'one') }
before { Fabricate(:tag, name: 'two') }
it 'runs a search for tags' do
results = subject.call('#one', limit: 5)
expect(results)
.to have_attributes(
size: 1,
first: eq(one)
)
end
end
end

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe TranslateStatusService, type: :service do
RSpec.describe TranslateStatusService do
subject(:service) { described_class.new }
let(:status) { Fabricate(:status, text: text, spoiler_text: spoiler_text, language: 'en', preloadable_poll: poll, media_attachments: media_attachments) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe UnallowDomainService, type: :service do
RSpec.describe UnallowDomainService do
subject { described_class.new }
let(:bad_domain) { 'evil.org' }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
describe UnblockDomainService, type: :service do
describe UnblockDomainService do
subject { described_class.new }
describe 'call' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe UnblockService, type: :service do
RSpec.describe UnblockService do
subject { described_class.new }
let(:sender) { Fabricate(:account, username: 'alice') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe UnfollowService, type: :service do
RSpec.describe UnfollowService do
subject { described_class.new }
let(:sender) { Fabricate(:account, username: 'alice') }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe UnsuspendAccountService, type: :service do
RSpec.describe UnsuspendAccountService do
shared_context 'with common context' do
subject { described_class.new.call(account) }

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe UpdateAccountService, type: :service do
RSpec.describe UpdateAccountService do
subject { described_class.new }
describe 'switching form locked to unlocked accounts', :sidekiq_inline do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe UpdateStatusService, type: :service do
RSpec.describe UpdateStatusService do
subject { described_class.new }
context 'when nothing changes' do

View File

@ -2,7 +2,7 @@
require 'rails_helper'
RSpec.describe VerifyLinkService, type: :service do
RSpec.describe VerifyLinkService do
subject { described_class.new }
context 'when given a local account' do