1
0
Fork 0
forked from gitea/nas

Merge remote-tracking branch 'parent/main' into kb_migration

This commit is contained in:
KMY 2023-05-23 09:34:55 +09:00
commit 1daa5d9096
42 changed files with 1235 additions and 255 deletions

View file

@ -21,12 +21,6 @@ Layout/ArgumentAlignment:
- 'config/initializers/cors.rb' - 'config/initializers/cors.rb'
- 'config/initializers/session_store.rb' - 'config/initializers/session_store.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowForAlignment, AllowBeforeTrailingComments, ForceEqualSignAlignment.
Layout/ExtraSpacing:
Exclude:
- 'config/initializers/omniauth.rb'
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle. # Configuration parameters: AllowMultipleStyles, EnforcedHashRocketStyle, EnforcedColonStyle, EnforcedLastArgumentHashStyle.
# SupportedHashRocketStyles: key, separator, table # SupportedHashRocketStyles: key, separator, table
@ -39,12 +33,6 @@ Layout/HashAlignment:
- 'config/initializers/rack_attack.rb' - 'config/initializers/rack_attack.rb'
- 'config/routes.rb' - 'config/routes.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: Width, AllowedPatterns.
Layout/IndentationWidth:
Exclude:
- 'config/initializers/ffmpeg.rb'
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment. # Configuration parameters: AllowDoxygenCommentStyle, AllowGemfileRubyComment.
Layout/LeadingCommentSpace: Layout/LeadingCommentSpace:
@ -52,14 +40,6 @@ Layout/LeadingCommentSpace:
- 'config/application.rb' - 'config/application.rb'
- 'config/initializers/omniauth.rb' - 'config/initializers/omniauth.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBraces.
# SupportedStyles: space, no_space
# SupportedStylesForEmptyBraces: space, no_space
Layout/SpaceBeforeBlockBraces:
Exclude:
- 'config/initializers/paperclip.rb'
# This cop supports safe autocorrection (--autocorrect). # This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle. # Configuration parameters: EnforcedStyle.
# SupportedStyles: require_no_space, require_space # SupportedStyles: require_no_space, require_space
@ -68,19 +48,6 @@ Layout/SpaceInLambdaLiteral:
- 'config/environments/production.rb' - 'config/environments/production.rb'
- 'config/initializers/content_security_policy.rb' - 'config/initializers/content_security_policy.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: EnforcedStyle.
# SupportedStyles: space, no_space
Layout/SpaceInsideStringInterpolation:
Exclude:
- 'config/initializers/webauthn.rb'
# This cop supports safe autocorrection (--autocorrect).
# Configuration parameters: AllowInHeredoc.
Layout/TrailingWhitespace:
Exclude:
- 'config/initializers/paperclip.rb'
# Configuration parameters: AllowedMethods, AllowedPatterns. # Configuration parameters: AllowedMethods, AllowedPatterns.
Lint/AmbiguousBlockAssociation: Lint/AmbiguousBlockAssociation:
Exclude: Exclude:
@ -618,7 +585,6 @@ RSpec/NoExpectationExample:
RSpec/PendingWithoutReason: RSpec/PendingWithoutReason:
Exclude: Exclude:
- 'spec/controllers/statuses_controller_spec.rb'
- 'spec/models/account_spec.rb' - 'spec/models/account_spec.rb'
# This cop supports unsafe autocorrection (--autocorrect-all). # This cop supports unsafe autocorrection (--autocorrect-all).
@ -634,10 +600,6 @@ RSpec/RepeatedExample:
Exclude: Exclude:
- 'spec/policies/status_policy_spec.rb' - 'spec/policies/status_policy_spec.rb'
RSpec/RepeatedExampleGroupBody:
Exclude:
- 'spec/controllers/statuses_controller_spec.rb'
RSpec/StubbedMock: RSpec/StubbedMock:
Exclude: Exclude:
- 'spec/controllers/api/base_controller_spec.rb' - 'spec/controllers/api/base_controller_spec.rb'

View file

@ -29,7 +29,7 @@ class Api::V1::Admin::DomainAllowsController < Api::BaseController
def create def create
authorize :domain_allow, :create? authorize :domain_allow, :create?
@domain_allow = DomainAllow.find_by(resource_params) @domain_allow = DomainAllow.find_by(domain: resource_params[:domain])
if @domain_allow.nil? if @domain_allow.nil?
@domain_allow = DomainAllow.create!(resource_params) @domain_allow = DomainAllow.create!(resource_params)

View file

@ -13,7 +13,7 @@ class Api::V1::FeaturedTagsController < Api::BaseController
end end
def create def create
featured_tag = CreateFeaturedTagService.new.call(current_account, featured_tag_params[:name]) featured_tag = CreateFeaturedTagService.new.call(current_account, params.require(:name))
render json: featured_tag, serializer: REST::FeaturedTagSerializer render json: featured_tag, serializer: REST::FeaturedTagSerializer
end end
@ -33,6 +33,6 @@ class Api::V1::FeaturedTagsController < Api::BaseController
end end
def featured_tag_params def featured_tag_params
params.permit(:name) params.require(:name)
end end
end end

View file

@ -127,7 +127,7 @@ class Auth::RegistrationsController < Devise::RegistrationsController
end end
def set_sessions def set_sessions
@sessions = current_user.session_activations @sessions = current_user.session_activations.order(updated_at: :desc)
end end
def set_strikes def set_strikes

View file

@ -9,6 +9,8 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
before_action :set_body_classes before_action :set_body_classes
before_action :set_cache_headers before_action :set_cache_headers
before_action :set_last_used_at_by_app, only: :index, unless: -> { request.format == :json }
skip_before_action :require_functional! skip_before_action :require_functional!
include Localized include Localized
@ -35,4 +37,14 @@ class Oauth::AuthorizedApplicationsController < Doorkeeper::AuthorizedApplicatio
def set_cache_headers def set_cache_headers
response.cache_control.replace(private: true, no_store: true) response.cache_control.replace(private: true, no_store: true)
end end
def set_last_used_at_by_app
@last_used_at_by_app = Doorkeeper::AccessToken
.select('DISTINCT ON (application_id) application_id, last_used_at')
.where(resource_owner_id: current_resource_owner.id)
.where.not(last_used_at: nil)
.order(application_id: :desc, last_used_at: :desc)
.pluck(:application_id, :last_used_at)
.to_h
end
end end

View file

@ -3,6 +3,8 @@ import PropTypes from 'prop-types';
import { supportsPassiveEvents } from 'detect-passive-events'; import { supportsPassiveEvents } from 'detect-passive-events';
import { scrollTop } from '../scroll'; import { scrollTop } from '../scroll';
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
export default class Column extends React.PureComponent { export default class Column extends React.PureComponent {
static propTypes = { static propTypes = {
@ -35,17 +37,17 @@ export default class Column extends React.PureComponent {
componentDidMount () { componentDidMount () {
if (this.props.bindToDocument) { if (this.props.bindToDocument) {
document.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false); document.addEventListener('wheel', this.handleWheel, listenerOptions);
} else { } else {
this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : false); this.node.addEventListener('wheel', this.handleWheel, listenerOptions);
} }
} }
componentWillUnmount () { componentWillUnmount () {
if (this.props.bindToDocument) { if (this.props.bindToDocument) {
document.removeEventListener('wheel', this.handleWheel); document.removeEventListener('wheel', this.handleWheel, listenerOptions);
} else { } else {
this.node.removeEventListener('wheel', this.handleWheel); this.node.removeEventListener('wheel', this.handleWheel, listenerOptions);
} }
} }

View file

@ -7,7 +7,7 @@ import { supportsPassiveEvents } from 'detect-passive-events';
import classNames from 'classnames'; import classNames from 'classnames';
import { CircularProgress } from 'mastodon/components/loading_indicator'; import { CircularProgress } from 'mastodon/components/loading_indicator';
const listenerOptions = supportsPassiveEvents ? { passive: true } : false; const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
let id = 0; let id = 0;
class DropdownMenu extends React.PureComponent { class DropdownMenu extends React.PureComponent {
@ -35,12 +35,13 @@ class DropdownMenu extends React.PureComponent {
handleDocumentClick = e => { handleDocumentClick = e => {
if (this.node && !this.node.contains(e.target)) { if (this.node && !this.node.contains(e.target)) {
this.props.onClose(); this.props.onClose();
e.stopPropagation();
} }
}; };
componentDidMount () { componentDidMount () {
document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('click', this.handleDocumentClick, { capture: true });
document.addEventListener('keydown', this.handleKeyDown, false); document.addEventListener('keydown', this.handleKeyDown, { capture: true });
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
if (this.focusedItem && this.props.openedViaKeyboard) { if (this.focusedItem && this.props.openedViaKeyboard) {
@ -49,8 +50,8 @@ class DropdownMenu extends React.PureComponent {
} }
componentWillUnmount () { componentWillUnmount () {
document.removeEventListener('click', this.handleDocumentClick, false); document.removeEventListener('click', this.handleDocumentClick, { capture: true });
document.removeEventListener('keydown', this.handleKeyDown, false); document.removeEventListener('keydown', this.handleKeyDown, { capture: true });
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
} }

View file

@ -15,6 +15,8 @@ import { connect } from 'react-redux';
const MOUSE_IDLE_DELAY = 300; const MOUSE_IDLE_DELAY = 300;
const listenerOptions = supportsPassiveEvents ? { passive: true } : false;
const mapStateToProps = (state, { scrollKey }) => { const mapStateToProps = (state, { scrollKey }) => {
return { return {
preventScroll: scrollKey === state.getIn(['dropdown_menu', 'scroll_key']), preventScroll: scrollKey === state.getIn(['dropdown_menu', 'scroll_key']),
@ -237,20 +239,20 @@ class ScrollableList extends PureComponent {
attachScrollListener () { attachScrollListener () {
if (this.props.bindToDocument) { if (this.props.bindToDocument) {
document.addEventListener('scroll', this.handleScroll); document.addEventListener('scroll', this.handleScroll);
document.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : undefined); document.addEventListener('wheel', this.handleWheel, listenerOptions);
} else { } else {
this.node.addEventListener('scroll', this.handleScroll); this.node.addEventListener('scroll', this.handleScroll);
this.node.addEventListener('wheel', this.handleWheel, supportsPassiveEvents ? { passive: true } : undefined); this.node.addEventListener('wheel', this.handleWheel, listenerOptions);
} }
} }
detachScrollListener () { detachScrollListener () {
if (this.props.bindToDocument) { if (this.props.bindToDocument) {
document.removeEventListener('scroll', this.handleScroll); document.removeEventListener('scroll', this.handleScroll);
document.removeEventListener('wheel', this.handleWheel); document.removeEventListener('wheel', this.handleWheel, listenerOptions);
} else { } else {
this.node.removeEventListener('scroll', this.handleScroll); this.node.removeEventListener('scroll', this.handleScroll);
this.node.removeEventListener('wheel', this.handleWheel); this.node.removeEventListener('wheel', this.handleWheel, listenerOptions);
} }
} }

View file

@ -1,5 +1,5 @@
import React, { PureComponent, Fragment } from 'react'; import React, { PureComponent, Fragment } from 'react';
import ReactDOM from 'react-dom'; import { createPortal } from 'react-dom';
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { IntlProvider, addLocaleData } from 'react-intl'; import { IntlProvider, addLocaleData } from 'react-intl';
import { fromJS } from 'immutable'; import { fromJS } from 'immutable';
@ -95,7 +95,7 @@ export default class MediaContainer extends PureComponent {
}), }),
}); });
return ReactDOM.createPortal( return createPortal(
<Component {...props} key={`media-${i}`} />, <Component {...props} key={`media-${i}`} />,
component, component,
); );

View file

@ -27,7 +27,7 @@ const messages = defineMessages({
let EmojiPicker, Emoji; // load asynchronously let EmojiPicker, Emoji; // load asynchronously
const listenerOptions = supportsPassiveEvents ? { passive: true } : false; const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
const backgroundImageFn = () => `${assetHost}/emoji/sheet_13.png`; const backgroundImageFn = () => `${assetHost}/emoji/sheet_13.png`;
@ -78,12 +78,12 @@ class ModifierPickerMenu extends React.PureComponent {
}; };
attachListeners () { attachListeners () {
document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('click', this.handleDocumentClick, { capture: true });
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
} }
removeListeners () { removeListeners () {
document.removeEventListener('click', this.handleDocumentClick, false); document.removeEventListener('click', this.handleDocumentClick, { capture: true });
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
} }
@ -176,7 +176,7 @@ class EmojiPickerMenuImpl extends React.PureComponent {
}; };
componentDidMount () { componentDidMount () {
document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('click', this.handleDocumentClick, { capture: true });
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
// Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need // Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
@ -191,7 +191,7 @@ class EmojiPickerMenuImpl extends React.PureComponent {
} }
componentWillUnmount () { componentWillUnmount () {
document.removeEventListener('click', this.handleDocumentClick, false); document.removeEventListener('click', this.handleDocumentClick, { capture: true });
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
} }

View file

@ -15,7 +15,7 @@ const messages = defineMessages({
clear: { id: 'emoji_button.clear', defaultMessage: 'Clear' }, clear: { id: 'emoji_button.clear', defaultMessage: 'Clear' },
}); });
const listenerOptions = supportsPassiveEvents ? { passive: true } : false; const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
class LanguageDropdownMenu extends React.PureComponent { class LanguageDropdownMenu extends React.PureComponent {
@ -39,11 +39,12 @@ class LanguageDropdownMenu extends React.PureComponent {
handleDocumentClick = e => { handleDocumentClick = e => {
if (this.node && !this.node.contains(e.target)) { if (this.node && !this.node.contains(e.target)) {
this.props.onClose(); this.props.onClose();
e.stopPropagation();
} }
}; };
componentDidMount () { componentDidMount () {
document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('click', this.handleDocumentClick, { capture: true });
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
// Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need // Because of https://github.com/react-bootstrap/react-bootstrap/issues/2614 we need
@ -57,7 +58,7 @@ class LanguageDropdownMenu extends React.PureComponent {
} }
componentWillUnmount () { componentWillUnmount () {
document.removeEventListener('click', this.handleDocumentClick, false); document.removeEventListener('click', this.handleDocumentClick, { capture: true });
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
} }

View file

@ -21,7 +21,7 @@ const messages = defineMessages({
change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' }, change_privacy: { id: 'privacy.change', defaultMessage: 'Adjust status privacy' },
}); });
const listenerOptions = supportsPassiveEvents ? { passive: true } : false; const listenerOptions = supportsPassiveEvents ? { passive: true, capture: true } : true;
class PrivacyDropdownMenu extends React.PureComponent { class PrivacyDropdownMenu extends React.PureComponent {
@ -36,6 +36,7 @@ class PrivacyDropdownMenu extends React.PureComponent {
handleDocumentClick = e => { handleDocumentClick = e => {
if (this.node && !this.node.contains(e.target)) { if (this.node && !this.node.contains(e.target)) {
this.props.onClose(); this.props.onClose();
e.stopPropagation();
} }
}; };
@ -93,13 +94,13 @@ class PrivacyDropdownMenu extends React.PureComponent {
}; };
componentDidMount () { componentDidMount () {
document.addEventListener('click', this.handleDocumentClick, false); document.addEventListener('click', this.handleDocumentClick, { capture: true });
document.addEventListener('touchend', this.handleDocumentClick, listenerOptions); document.addEventListener('touchend', this.handleDocumentClick, listenerOptions);
if (this.focusedItem) this.focusedItem.focus({ preventScroll: true }); if (this.focusedItem) this.focusedItem.focus({ preventScroll: true });
} }
componentWillUnmount () { componentWillUnmount () {
document.removeEventListener('click', this.handleDocumentClick, false); document.removeEventListener('click', this.handleDocumentClick, { capture: true });
document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions); document.removeEventListener('touchend', this.handleDocumentClick, listenerOptions);
} }

View file

@ -85,7 +85,7 @@ class EmbedModal extends ImmutablePureComponent {
className='embed-modal__iframe' className='embed-modal__iframe'
frameBorder='0' frameBorder='0'
ref={this.setIframeRef} ref={this.setIframeRef}
sandbox='allow-same-origin' sandbox='allow-scripts allow-same-origin'
title='preview' title='preview'
/> />
</div> </div>

View file

@ -1,5 +1,5 @@
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
import { setupBrowserNotifications } from 'mastodon/actions/notifications'; import { setupBrowserNotifications } from 'mastodon/actions/notifications';
import Mastodon from 'mastodon/containers/mastodon'; import Mastodon from 'mastodon/containers/mastodon';
import { store } from 'mastodon/store'; import { store } from 'mastodon/store';
@ -17,7 +17,8 @@ function main() {
const mountNode = document.getElementById('mastodon'); const mountNode = document.getElementById('mastodon');
const props = JSON.parse(mountNode.getAttribute('data-props')); const props = JSON.parse(mountNode.getAttribute('data-props'));
ReactDOM.render(<Mastodon {...props} />, mountNode); const root = createRoot(mountNode);
root.render(<Mastodon {...props} />);
store.dispatch(setupBrowserNotifications()); store.dispatch(setupBrowserNotifications());
if (process.env.NODE_ENV === 'production' && me && 'serviceWorker' in navigator) { if (process.env.NODE_ENV === 'production' && me && 'serviceWorker' in navigator) {

View file

@ -2,7 +2,7 @@ import './public-path';
import { delegate } from '@rails/ujs'; import { delegate } from '@rails/ujs';
import ready from '../mastodon/ready'; import ready from '../mastodon/ready';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
const setAnnouncementEndsAttributes = (target) => { const setAnnouncementEndsAttributes = (target) => {
const valid = target?.value && target?.validity?.valid; const valid = target?.value && target?.validity?.valid;
@ -241,11 +241,13 @@ ready(() => {
import('../mastodon/containers/admin_component').then(({ default: AdminComponent }) => { import('../mastodon/containers/admin_component').then(({ default: AdminComponent }) => {
return import('../mastodon/components/admin/' + componentName).then(({ default: Component }) => { return import('../mastodon/components/admin/' + componentName).then(({ default: Component }) => {
ReactDOM.render(( const root = createRoot(element);
root.render (
<AdminComponent locale={locale}> <AdminComponent locale={locale}>
<Component {...componentProps} /> <Component {...componentProps} />
</AdminComponent> </AdminComponent>,
), element); );
}); });
}).catch(error => { }).catch(error => {
console.error(error); console.error(error);

View file

@ -15,7 +15,7 @@ import { delegate } from '@rails/ujs';
import emojify from '../mastodon/features/emoji/emoji'; import emojify from '../mastodon/features/emoji/emoji';
import { getLocale } from '../mastodon/locales'; import { getLocale } from '../mastodon/locales';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
import { createBrowserHistory } from 'history'; import { createBrowserHistory } from 'history';
start(); start();
@ -153,7 +153,8 @@ function loaded() {
const content = document.createElement('div'); const content = document.createElement('div');
ReactDOM.render(<MediaContainer locale={locale} components={reactComponents} />, content); const root = createRoot(content);
root.render(<MediaContainer locale={locale} components={reactComponents} />);
document.body.appendChild(content); document.body.appendChild(content);
scrollToDetailedStatus(); scrollToDetailedStatus();
}) })

View file

@ -4,7 +4,7 @@ import { start } from '../mastodon/common';
import ready from '../mastodon/ready'; import ready from '../mastodon/ready';
import ComposeContainer from '../mastodon/containers/compose_container'; import ComposeContainer from '../mastodon/containers/compose_container';
import React from 'react'; import React from 'react';
import ReactDOM from 'react-dom'; import { createRoot } from 'react-dom/client';
start(); start();
@ -16,7 +16,8 @@ function loaded() {
if(!attr) return; if(!attr) return;
const props = JSON.parse(attr); const props = JSON.parse(attr);
ReactDOM.render(<ComposeContainer {...props} />, mountNode); const root = createRoot(mountNode);
root.render(<ComposeContainer {...props} />);
} }
} }

View file

@ -14,11 +14,6 @@ declare module '*.jpg' {
export default path; export default path;
} }
declare module '*.jpg' {
const path: string;
export default path;
}
declare module '*.png' { declare module '*.png' {
const path: string; const path: string;
export default path; export default path;

View file

@ -16,7 +16,7 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity
@account, @account,
target_account, target_account,
status_ids: target_statuses.nil? ? [] : target_statuses.map(&:id), status_ids: target_statuses.nil? ? [] : target_statuses.map(&:id),
comment: @json['content'] || '', comment: report_comment,
uri: report_uri uri: report_uri
) )
end end
@ -35,4 +35,8 @@ class ActivityPub::Activity::Flag < ActivityPub::Activity
def report_uri def report_uri
@json['id'] unless @json['id'].nil? || non_matching_uri_hosts?(@account.uri, @json['id']) @json['id'] unless @json['id'].nil? || non_matching_uri_hosts?(@account.uri, @json['id'])
end end
def report_comment
(@json['content'] || '')[0...5000]
end
end end

View file

@ -28,6 +28,8 @@ class ActivityPub::TagManager
return activity_account_status_url(target.account, target) if target.reblog? return activity_account_status_url(target.account, target) if target.reblog?
short_account_status_url(target.account, target) short_account_status_url(target.account, target)
when :flag
target.uri
end end
end end
@ -47,6 +49,8 @@ class ActivityPub::TagManager
emoji_url(target) emoji_url(target)
when :emoji_reaction when :emoji_reaction
emoji_reaction_url(target) emoji_reaction_url(target)
when :flag
target.uri
end end
end end

View file

@ -9,10 +9,6 @@ module ApplicationExtension
validates :redirect_uri, length: { maximum: 2_000 } validates :redirect_uri, length: { maximum: 2_000 }
end end
def most_recently_used_access_token
@most_recently_used_access_token ||= access_tokens.where.not(last_used_at: nil).order(last_used_at: :desc).first
end
def confirmation_redirect_uri def confirmation_redirect_uri
redirect_uri.lines.first.strip redirect_uri.lines.first.strip
end end

View file

@ -140,7 +140,7 @@ class LinkDetailsExtractor
end end
def html def html
player_url.present? ? content_tag(:iframe, nil, src: player_url, width: width, height: height, allowtransparency: 'true', scrolling: 'no', frameborder: '0') : nil player_url.present? ? content_tag(:iframe, nil, src: player_url, width: width, height: height, allowfullscreen: 'true', allowtransparency: 'true', scrolling: 'no', frameborder: '0') : nil
end end
def width def width

View file

@ -40,7 +40,10 @@ class Report < ApplicationRecord
scope :resolved, -> { where.not(action_taken_at: nil) } scope :resolved, -> { where.not(action_taken_at: nil) }
scope :with_accounts, -> { includes([:account, :target_account, :action_taken_by_account, :assigned_account].index_with({ user: [:invite_request, :invite] })) } scope :with_accounts, -> { includes([:account, :target_account, :action_taken_by_account, :assigned_account].index_with({ user: [:invite_request, :invite] })) }
validates :comment, length: { maximum: 1_000 } # A report is considered local if the reporter is local
delegate :local?, to: :account
validates :comment, length: { maximum: 1_000 }, if: :local?
validates :rule_ids, absence: true, unless: :violation? validates :rule_ids, absence: true, unless: :violation?
validate :validate_rule_ids validate :validate_rule_ids
@ -51,10 +54,6 @@ class Report < ApplicationRecord
violation: 2_000, violation: 2_000,
} }
def local?
false # Force uri_for to use uri attribute
end
before_validation :set_uri, only: :create before_validation :set_uri, only: :create
after_create_commit :trigger_webhooks after_create_commit :trigger_webhooks

View file

@ -5,6 +5,7 @@
= render 'auth/shared/progress', stage: 'confirm' = render 'auth/shared/progress', stage: 'confirm'
= hidden_field_tag :confirmation_token, params[:confirmation_token] = hidden_field_tag :confirmation_token, params[:confirmation_token]
= hidden_field_tag :redirect_to_app, params[:redirect_to_app]
%p.lead= t('auth.captcha_confirmation.hint_html') %p.lead= t('auth.captcha_confirmation.hint_html')

View file

@ -18,8 +18,8 @@
.announcements-list__item__action-bar .announcements-list__item__action-bar
.announcements-list__item__meta .announcements-list__item__meta
- if application.most_recently_used_access_token - if @last_used_at_by_app[application.id]
= t('doorkeeper.authorized_applications.index.last_used_at', date: l(application.most_recently_used_access_token.last_used_at.to_date)) = t('doorkeeper.authorized_applications.index.last_used_at', date: l(@last_used_at_by_app[application.id].to_date))
- else - else
= t('doorkeeper.authorized_applications.index.never_used') = t('doorkeeper.authorized_applications.index.never_used')

View file

@ -9,7 +9,6 @@ const config = {
'<rootDir>/public/', '<rootDir>/public/',
'<rootDir>/tmp/', '<rootDir>/tmp/',
], ],
setupFiles: ['raf/polyfill'],
setupFilesAfterEnv: ['<rootDir>/app/javascript/mastodon/test_setup.js'], setupFilesAfterEnv: ['<rootDir>/app/javascript/mastodon/test_setup.js'],
collectCoverageFrom: [ collectCoverageFrom: [
'app/javascript/mastodon/**/*.{js,jsx,ts,tsx}', 'app/javascript/mastodon/**/*.{js,jsx,ts,tsx}',

View file

@ -87,8 +87,8 @@
"postcss-loader": "^4.3.0", "postcss-loader": "^4.3.0",
"prop-types": "^15.8.1", "prop-types": "^15.8.1",
"punycode": "^2.3.0", "punycode": "^2.3.0",
"react": "^16.14.0", "react": "^18.2.0",
"react-dom": "^16.14.0", "react-dom": "^18.2.0",
"react-helmet": "^6.1.0", "react-helmet": "^6.1.0",
"react-hotkeys": "^1.1.4", "react-hotkeys": "^1.1.4",
"react-immutable-proptypes": "^2.2.0", "react-immutable-proptypes": "^2.2.0",
@ -140,7 +140,7 @@
}, },
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "^5.16.5", "@testing-library/jest-dom": "^5.16.5",
"@testing-library/react": "^12.1.5", "@testing-library/react": "^14.0.0",
"@types/babel__core": "^7.20.0", "@types/babel__core": "^7.20.0",
"@types/emoji-mart": "^3.0.9", "@types/emoji-mart": "^3.0.9",
"@types/escape-html": "^1.0.2", "@types/escape-html": "^1.0.2",
@ -155,9 +155,8 @@
"@types/pg": "^8.6.6", "@types/pg": "^8.6.6",
"@types/prop-types": "^15.7.5", "@types/prop-types": "^15.7.5",
"@types/punycode": "^2.1.0", "@types/punycode": "^2.1.0",
"@types/raf": "^3.4.0", "@types/react": "^18.0.26",
"@types/react": "^16.14.38", "@types/react-dom": "^18.2.4",
"@types/react-dom": "^16.9.18",
"@types/react-helmet": "^6.1.6", "@types/react-helmet": "^6.1.6",
"@types/react-immutable-proptypes": "^2.1.0", "@types/react-immutable-proptypes": "^2.1.0",
"@types/react-intl": "2.3.18", "@types/react-intl": "2.3.18",
@ -195,9 +194,8 @@
"jest-environment-jsdom": "^29.5.0", "jest-environment-jsdom": "^29.5.0",
"lint-staged": "^13.2.2", "lint-staged": "^13.2.2",
"prettier": "^2.8.8", "prettier": "^2.8.8",
"raf": "^3.4.1",
"react-intl-translations-manager": "^5.0.3", "react-intl-translations-manager": "^5.0.3",
"react-test-renderer": "^16.14.0", "react-test-renderer": "^18.2.0",
"stylelint": "^15.6.1", "stylelint": "^15.6.1",
"stylelint-config-standard-scss": "^9.0.0", "stylelint-config-standard-scss": "^9.0.0",
"typescript": "^5.0.4", "typescript": "^5.0.4",
@ -205,6 +203,7 @@
"yargs": "^17.7.2" "yargs": "^17.7.2"
}, },
"resolutions": { "resolutions": {
"@types/react": "^18.0.26",
"kind-of": "^6.0.3", "kind-of": "^6.0.3",
"webpack/terser-webpack-plugin": "^4.2.3" "webpack/terser-webpack-plugin": "^4.2.3"
}, },

View file

@ -5,23 +5,182 @@ require 'rails_helper'
describe Api::V1::Admin::CanonicalEmailBlocksController do describe Api::V1::Admin::CanonicalEmailBlocksController do
render_views render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) } let(:role) { UserRole.find_by(name: 'Admin') }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') } let(:user) { Fabricate(:user, role: role) }
let(:account) { Fabricate(:account) } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'admin:read:canonical_email_blocks admin:write:canonical_email_blocks' }
before do before do
allow(controller).to receive(:doorkeeper_token) { token } allow(controller).to receive(:doorkeeper_token) { token }
end end
shared_examples 'forbidden for wrong scope' do |wrong_scope|
let(:scopes) { wrong_scope }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
shared_examples 'forbidden for wrong role' do |wrong_role|
let(:role) { UserRole.find_by(name: wrong_role) }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
describe 'GET #index' do describe 'GET #index' do
context 'with wrong scope' do
before do
get :index
end
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
end
context 'with wrong role' do
before do
get :index
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
it 'returns http success' do it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 } get :index
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
context 'when there is no canonical email block' do
it 'returns an empty list' do
get :index
body = body_as_json
expect(body).to be_empty
end
end
context 'when there are canonical email blocks' do
let!(:canonical_email_blocks) { Fabricate.times(5, :canonical_email_block) }
let(:expected_email_hashes) { canonical_email_blocks.pluck(:canonical_email_hash) }
it 'returns the correct canonical email hashes' do
get :index
json = body_as_json
expect(json.pluck(:canonical_email_hash)).to match_array(expected_email_hashes)
end
context 'with limit param' do
let(:params) { { limit: 2 } }
it 'returns only the requested number of canonical email blocks' do
get :index, params: params
json = body_as_json
expect(json.size).to eq(params[:limit])
end
end
context 'with since_id param' do
let(:params) { { since_id: canonical_email_blocks[1].id } }
it 'returns only the canonical email blocks after since_id' do
get :index, params: params
canonical_email_blocks_ids = canonical_email_blocks.pluck(:id).map(&:to_s)
json = body_as_json
expect(json.pluck(:id)).to match_array(canonical_email_blocks_ids[2..])
end
end
context 'with max_id param' do
let(:params) { { max_id: canonical_email_blocks[3].id } }
it 'returns only the canonical email blocks before max_id' do
get :index, params: params
canonical_email_blocks_ids = canonical_email_blocks.pluck(:id).map(&:to_s)
json = body_as_json
expect(json.pluck(:id)).to match_array(canonical_email_blocks_ids[..2])
end
end
end
end
describe 'GET #show' do
let!(:canonical_email_block) { Fabricate(:canonical_email_block) }
let(:params) { { id: canonical_email_block.id } }
context 'with wrong scope' do
before do
get :show, params: params
end
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
end
context 'with wrong role' do
before do
get :show, params: params
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
context 'when canonical email block exists' do
it 'returns http success' do
get :show, params: params
expect(response).to have_http_status(200)
end
it 'returns canonical email block data correctly' do
get :show, params: params
json = body_as_json
expect(json[:id]).to eq(canonical_email_block.id.to_s)
expect(json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
end
end
context 'when canonical block does not exist' do
it 'returns http not found' do
get :show, params: { id: 0 }
expect(response).to have_http_status(404)
end
end
end end
describe 'POST #test' do describe 'POST #test' do
context 'with wrong scope' do
before do
post :test
end
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
end
context 'with wrong role' do
before do
post :test, params: { email: 'whatever@email.com' }
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
context 'when required email is not provided' do context 'when required email is not provided' do
it 'returns http bad request' do it 'returns http bad request' do
post :test post :test
@ -68,4 +227,132 @@ describe Api::V1::Admin::CanonicalEmailBlocksController do
end end
end end
end end
describe 'POST #create' do
let(:params) { { email: 'example@email.com' } }
let(:canonical_email_block) { CanonicalEmailBlock.new(email: params[:email]) }
context 'with wrong scope' do
before do
post :create, params: params
end
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
end
context 'with wrong role' do
before do
post :create, params: params
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
it 'returns http success' do
post :create, params: params
expect(response).to have_http_status(200)
end
it 'returns canonical_email_hash correctly' do
post :create, params: params
json = body_as_json
expect(json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
end
context 'when required email param is not provided' do
it 'returns http unprocessable entity' do
post :create
expect(response).to have_http_status(422)
end
end
context 'when canonical_email_hash param is provided instead of email' do
let(:params) { { canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } }
it 'returns http success' do
post :create, params: params
expect(response).to have_http_status(200)
end
it 'returns correct canonical_email_hash' do
post :create, params: params
json = body_as_json
expect(json[:canonical_email_hash]).to eq(params[:canonical_email_hash])
end
end
context 'when both email and canonical_email_hash params are provided' do
let(:params) { { email: 'example@email.com', canonical_email_hash: 'dd501ce4e6b08698f19df96f2f15737e48a75660b1fa79b6ff58ea25ee4851a4' } }
it 'returns http success' do
post :create, params: params
expect(response).to have_http_status(200)
end
it 'ignores canonical_email_hash param' do
post :create, params: params
json = body_as_json
expect(json[:canonical_email_hash]).to eq(canonical_email_block.canonical_email_hash)
end
end
context 'when canonical email was already blocked' do
before do
canonical_email_block.save
end
it 'returns http unprocessable entity' do
post :create, params: params
expect(response).to have_http_status(422)
end
end
end
describe 'DELETE #destroy' do
let!(:canonical_email_block) { Fabricate(:canonical_email_block) }
let(:params) { { id: canonical_email_block.id } }
context 'with wrong scope' do
before do
delete :destroy, params: params
end
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
end
context 'with wrong role' do
before do
delete :destroy, params: params
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
it 'returns http success' do
delete :destroy, params: params
expect(response).to have_http_status(200)
end
context 'when canonical email block is not found' do
it 'returns http not found' do
delete :destroy, params: { id: 0 }
expect(response).to have_http_status(404)
end
end
end
end end

View file

@ -128,5 +128,13 @@ RSpec.describe Api::V1::Admin::DomainAllowsController do
expect(response).to have_http_status(422) expect(response).to have_http_status(422)
end end
end end
context 'when domain name is not specified' do
it 'returns http unprocessable entity' do
post :create
expect(response).to have_http_status(422)
end
end
end end
end end

View file

@ -5,19 +5,280 @@ require 'rails_helper'
describe Api::V1::Admin::EmailDomainBlocksController do describe Api::V1::Admin::EmailDomainBlocksController do
render_views render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) } let(:role) { UserRole.find_by(name: 'Admin') }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') } let(:user) { Fabricate(:user, role: role) }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:account) { Fabricate(:account) } let(:account) { Fabricate(:account) }
let(:scopes) { 'admin:read:email_domain_blocks admin:write:email_domain_blocks' }
before do before do
allow(controller).to receive(:doorkeeper_token) { token } allow(controller).to receive(:doorkeeper_token) { token }
end end
shared_examples 'forbidden for wrong scope' do |wrong_scope|
let(:scopes) { wrong_scope }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
shared_examples 'forbidden for wrong role' do |wrong_role|
let(:role) { UserRole.find_by(name: wrong_role) }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
describe 'GET #index' do describe 'GET #index' do
context 'with wrong scope' do
before do
get :index
end
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
end
context 'with wrong role' do
before do
get :index
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
it 'returns http success' do it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 } get :index
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
context 'when there is no email domain block' do
it 'returns an empty list' do
get :index
json = body_as_json
expect(json).to be_empty
end
end
context 'when there are email domain blocks' do
let!(:email_domain_blocks) { Fabricate.times(5, :email_domain_block) }
let(:blocked_email_domains) { email_domain_blocks.pluck(:domain) }
it 'return the correct blocked email domains' do
get :index
json = body_as_json
expect(json.pluck(:domain)).to match_array(blocked_email_domains)
end
context 'with limit param' do
let(:params) { { limit: 2 } }
it 'returns only the requested number of email domain blocks' do
get :index, params: params
json = body_as_json
expect(json.size).to eq(params[:limit])
end
end
context 'with since_id param' do
let(:params) { { since_id: email_domain_blocks[1].id } }
it 'returns only the email domain blocks after since_id' do
get :index, params: params
email_domain_blocks_ids = email_domain_blocks.pluck(:id).map(&:to_s)
json = body_as_json
expect(json.pluck(:id)).to match_array(email_domain_blocks_ids[2..])
end
end
context 'with max_id param' do
let(:params) { { max_id: email_domain_blocks[3].id } }
it 'returns only the email domain blocks before max_id' do
get :index, params: params
email_domain_blocks_ids = email_domain_blocks.pluck(:id).map(&:to_s)
json = body_as_json
expect(json.pluck(:id)).to match_array(email_domain_blocks_ids[..2])
end
end
end
end
describe 'GET #show' do
let!(:email_domain_block) { Fabricate(:email_domain_block) }
let(:params) { { id: email_domain_block.id } }
context 'with wrong scope' do
before do
get :show, params: params
end
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
end
context 'with wrong role' do
before do
get :show, params: params
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
context 'when email domain block exists' do
it 'returns http success' do
get :show, params: params
expect(response).to have_http_status(200)
end
it 'returns the correct blocked domain' do
get :show, params: params
json = body_as_json
expect(json[:domain]).to eq(email_domain_block.domain)
end
end
context 'when email domain block does not exist' do
it 'returns http not found' do
get :show, params: { id: 0 }
expect(response).to have_http_status(404)
end
end
end
describe 'POST #create' do
let(:params) { { domain: 'example.com' } }
context 'with wrong scope' do
before do
post :create, params: params
end
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
end
context 'with wrong role' do
before do
post :create, params: params
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
it 'returns http success' do
post :create, params: params
expect(response).to have_http_status(200)
end
it 'returns the correct blocked email domain' do
post :create, params: params
json = body_as_json
expect(json[:domain]).to eq(params[:domain])
end
context 'when domain param is not provided' do
let(:params) { { domain: '' } }
it 'returns http unprocessable entity' do
post :create, params: params
expect(response).to have_http_status(422)
end
end
context 'when provided domain name has an invalid character' do
let(:params) { { domain: 'do\uD800.com' } }
it 'returns http unprocessable entity' do
post :create, params: params
expect(response).to have_http_status(422)
end
end
context 'when provided domain is already blocked' do
before do
EmailDomainBlock.create(params)
end
it 'returns http unprocessable entity' do
post :create, params: params
expect(response).to have_http_status(422)
end
end
end
describe 'DELETE #destroy' do
let!(:email_domain_block) { Fabricate(:email_domain_block) }
let(:params) { { id: email_domain_block.id } }
context 'with wrong scope' do
before do
delete :destroy, params: params
end
it_behaves_like 'forbidden for wrong scope', 'read:statuses'
end
context 'with wrong role' do
before do
delete :destroy, params: params
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
it 'returns http success' do
delete :destroy, params: params
expect(response).to have_http_status(200)
end
it 'returns an empty body' do
delete :destroy, params: params
json = body_as_json
expect(json).to be_empty
end
it 'deletes email domain block' do
delete :destroy, params: params
email_domain_block = EmailDomainBlock.find_by(id: params[:id])
expect(email_domain_block).to be_nil
end
context 'when email domain block does not exist' do
it 'returns http not found' do
delete :destroy, params: { id: 0 }
expect(response).to have_http_status(404)
end
end
end end
end end

View file

@ -5,19 +5,305 @@ require 'rails_helper'
describe Api::V1::Admin::IpBlocksController do describe Api::V1::Admin::IpBlocksController do
render_views render_views
let(:user) { Fabricate(:user, role: UserRole.find_by(name: 'Admin')) } let(:role) { UserRole.find_by(name: 'Admin') }
let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: 'admin:read') } let(:user) { Fabricate(:user, role: role) }
let(:account) { Fabricate(:account) } let(:token) { Fabricate(:accessible_access_token, resource_owner_id: user.id, scopes: scopes) }
let(:scopes) { 'admin:read:ip_blocks admin:write:ip_blocks' }
before do before do
allow(controller).to receive(:doorkeeper_token) { token } allow(controller).to receive(:doorkeeper_token) { token }
end end
shared_examples 'forbidden for wrong scope' do |wrong_scope|
let(:scopes) { wrong_scope }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
shared_examples 'forbidden for wrong role' do |wrong_role|
let(:role) { UserRole.find_by(name: wrong_role) }
it 'returns http forbidden' do
expect(response).to have_http_status(403)
end
end
describe 'GET #index' do describe 'GET #index' do
context 'with wrong scope' do
before do
get :index
end
it_behaves_like 'forbidden for wrong scope', 'admin:write:ip_blocks'
end
context 'with wrong role' do
before do
get :index
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
it 'returns http success' do it 'returns http success' do
get :index, params: { account_id: account.id, limit: 2 } get :index
expect(response).to have_http_status(200) expect(response).to have_http_status(200)
end end
context 'when there is no ip block' do
it 'returns an empty body' do
get :index
json = body_as_json
expect(json).to be_empty
end
end
context 'when there are ip blocks' do
let!(:ip_blocks) do
[
IpBlock.create(ip: '192.0.2.0/24', severity: :no_access),
IpBlock.create(ip: '172.16.0.1', severity: :sign_up_requires_approval, comment: 'Spam'),
IpBlock.create(ip: '2001:0db8::/32', severity: :sign_up_block, expires_in: 10.days),
]
end
let(:expected_response) do
ip_blocks.map do |ip_block|
{
id: ip_block.id.to_s,
ip: ip_block.ip,
severity: ip_block.severity.to_s,
comment: ip_block.comment,
created_at: ip_block.created_at.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
expires_at: ip_block.expires_at&.strftime('%Y-%m-%dT%H:%M:%S.%LZ'),
}
end
end
it 'returns the correct blocked ips' do
get :index
json = body_as_json
expect(json).to match_array(expected_response)
end
context 'with limit param' do
let(:params) { { limit: 2 } }
it 'returns only the requested number of ip blocks' do
get :index, params: params
json = body_as_json
expect(json.size).to eq(params[:limit])
end
end
end
end
describe 'GET #show' do
let!(:ip_block) { IpBlock.create(ip: '192.0.2.0/24', severity: :no_access) }
let(:params) { { id: ip_block.id } }
context 'with wrong scope' do
before do
get :show, params: params
end
it_behaves_like 'forbidden for wrong scope', 'admin:write:ip_blocks'
end
context 'with wrong role' do
before do
get :show, params: params
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
it 'returns http success' do
get :show, params: params
expect(response).to have_http_status(200)
end
it 'returns the correct ip block' do
get :show, params: params
json = body_as_json
expect(json[:ip]).to eq("#{ip_block.ip}/#{ip_block.ip.prefix}")
expect(json[:severity]).to eq(ip_block.severity.to_s)
end
context 'when ip block does not exist' do
it 'returns http not found' do
get :show, params: { id: 0 }
expect(response).to have_http_status(404)
end
end
end
describe 'POST #create' do
let(:params) { { ip: '151.0.32.55', severity: 'no_access', comment: 'Spam' } }
context 'with wrong scope' do
before do
post :create, params: params
end
it_behaves_like 'forbidden for wrong scope', 'admin:read:ip_blocks'
end
context 'with wrong role' do
before do
post :create, params: params
end
it_behaves_like 'forbidden for wrong role', ''
it_behaves_like 'forbidden for wrong role', 'Moderator'
end
it 'returns http success' do
post :create, params: params
expect(response).to have_http_status(200)
end
it 'returns the correct ip block' do
post :create, params: params
json = body_as_json
expect(json[:ip]).to eq("#{params[:ip]}/32")
expect(json[:severity]).to eq(params[:severity])
expect(json[:comment]).to eq(params[:comment])
end
context 'when ip is not provided' do
let(:params) { { ip: '', severity: 'no_access' } }
it 'returns http unprocessable entity' do
post :create, params: params
expect(response).to have_http_status(422)
end
end
context 'when severity is not provided' do
let(:params) { { ip: '173.65.23.1', severity: '' } }
it 'returns http unprocessable entity' do
post :create, params: params
expect(response).to have_http_status(422)
end
end
context 'when provided ip is already blocked' do
before do
IpBlock.create(params)
end
it 'returns http unprocessable entity' do
post :create, params: params
expect(response).to have_http_status(422)
end
end
context 'when provided ip address is invalid' do
let(:params) { { ip: '520.13.54.120', severity: 'no_access' } }
it 'returns http unprocessable entity' do
post :create, params: params
expect(response).to have_http_status(422)
end
end
end
describe 'PUT #update' do
context 'when ip block exists' do
let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access', comment: 'Spam', expires_in: 48.hours) }
let(:params) { { id: ip_block.id, severity: 'sign_up_requires_approval', comment: 'Decreasing severity' } }
it 'returns http success' do
put :update, params: params
expect(response).to have_http_status(200)
end
it 'returns the correct ip block' do
put :update, params: params
json = body_as_json
expect(json).to match(hash_including({
ip: "#{ip_block.ip}/#{ip_block.ip.prefix}",
severity: 'sign_up_requires_approval',
comment: 'Decreasing severity',
}))
end
it 'updates the severity correctly' do
expect { put :update, params: params }.to change { ip_block.reload.severity }.from('no_access').to('sign_up_requires_approval')
end
it 'updates the comment correctly' do
expect { put :update, params: params }.to change { ip_block.reload.comment }.from('Spam').to('Decreasing severity')
end
end
context 'when ip block does not exist' do
it 'returns http not found' do
put :update, params: { id: 0 }
expect(response).to have_http_status(404)
end
end
end
describe 'DELETE #destroy' do
context 'when ip block exists' do
let!(:ip_block) { IpBlock.create(ip: '185.200.13.3', severity: 'no_access') }
let(:params) { { id: ip_block.id } }
it 'returns http success' do
delete :destroy, params: params
expect(response).to have_http_status(200)
end
it 'returns an empty body' do
delete :destroy, params: params
json = body_as_json
expect(json).to be_empty
end
it 'deletes the ip block' do
delete :destroy, params: params
expect(IpBlock.find_by(id: ip_block.id)).to be_nil
end
end
context 'when ip block does not exist' do
it 'returns http not found' do
delete :destroy, params: { id: 0 }
expect(response).to have_http_status(404)
end
end
end end
end end

View file

@ -719,65 +719,180 @@ describe StatusesController do
end end
context 'when status is public' do context 'when status is public' do
pending before do
status.update(visibility: :public)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(:success)
end
end end
context 'when status is private' do context 'when status is private' do
pending before do
status.update(visibility: :private)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end end
context 'when status is direct' do context 'when status is direct' do
pending before do
status.update(visibility: :direct)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end end
context 'when signed-in' do context 'when signed-in' do
let(:user) { Fabricate(:user) }
before do
sign_in(user)
end
context 'when status is public' do context 'when status is public' do
pending before do
status.update(visibility: :public)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(:success)
end
end end
context 'when status is private' do context 'when status is private' do
before do
status.update(visibility: :private)
end
context 'when user is authorized to see it' do context 'when user is authorized to see it' do
pending before do
user.account.follow!(account)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
end end
context 'when user is not authorized to see it' do context 'when user is not authorized to see it' do
pending before do
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end end
end end
context 'when status is direct' do context 'when status is direct' do
before do
status.update(visibility: :direct)
end
context 'when user is authorized to see it' do context 'when user is authorized to see it' do
pending before do
Fabricate(:mention, account: user.account, status: status)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
end end
context 'when user is not authorized to see it' do context 'when user is not authorized to see it' do
pending before do
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end end
end end
end end
context 'with signature' do context 'with signature' do
let(:remote_account) { Fabricate(:account, domain: 'example.com') }
before do
allow(controller).to receive(:signed_request_actor).and_return(remote_account)
end
context 'when status is public' do context 'when status is public' do
pending before do
status.update(visibility: :public)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(:success)
end
end end
context 'when status is private' do context 'when status is private' do
before do
status.update(visibility: :private)
end
context 'when user is authorized to see it' do context 'when user is authorized to see it' do
pending before do
remote_account.follow!(account)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
end end
context 'when user is not authorized to see it' do context 'when user is not authorized to see it' do
pending before do
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end end
end end
context 'when status is direct' do context 'when status is direct' do
before do
status.update(visibility: :direct)
end
context 'when user is authorized to see it' do context 'when user is authorized to see it' do
pending before do
Fabricate(:mention, account: remote_account, status: status)
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http success' do
expect(response).to have_http_status(200)
end
end end
context 'when user is not authorized to see it' do context 'when user is not authorized to see it' do
pending before do
get :activity, params: { account_username: account.username, id: status.id }
end
it 'returns http not_found' do
expect(response).to have_http_status(404)
end
end end
end end
end end

View file

@ -1,6 +1,6 @@
# frozen_string_literal: true # frozen_string_literal: true
Fabricator(:canonical_email_block) do Fabricator(:canonical_email_block) do
email 'test@example.com' email { sequence(:email) { |i| "#{i}#{Faker::Internet.email}" } }
reference_account { Fabricate(:account) } reference_account { Fabricate(:account) }
end end

View file

@ -0,0 +1,35 @@
# frozen_string_literal: true
require 'rails_helper'
describe 'email confirmation flow when captcha is enabled' do
let(:user) { Fabricate(:user, confirmed_at: nil, confirmation_token: 'foobar', created_by_application: client_app) }
let(:client_app) { nil }
before do
# rubocop:disable RSpec/AnyInstance -- easiest way to deal with that that I know of
allow_any_instance_of(Auth::ConfirmationsController).to receive(:captcha_enabled?).and_return(true)
allow_any_instance_of(Auth::ConfirmationsController).to receive(:check_captcha!).and_return(true)
allow_any_instance_of(Auth::ConfirmationsController).to receive(:render_captcha).and_return(nil)
# rubocop:enable RSpec/AnyInstance
end
context 'when the user signed up through an app' do
let(:client_app) { Fabricate(:application) }
it 'logs in' do
visit "/auth/confirmation?confirmation_token=#{user.confirmation_token}&redirect_to_app=true"
# It presents the user with a captcha form
expect(page).to have_title(I18n.t('auth.captcha_confirmation.title'))
# It does not confirm the user just yet
expect(user.reload.confirmed?).to be false
# It redirects to app and confirms user
click_on I18n.t('challenge.confirm')
expect(user.reload.confirmed?).to be true
expect(page).to have_current_path(/\A#{client_app.confirmation_redirect_uri}/, url: true)
end
end
end

View file

@ -39,6 +39,37 @@ RSpec.describe ActivityPub::Activity::Flag do
end end
end end
context 'when the report comment is excessively long' do
subject do
described_class.new({
'@context': 'https://www.w3.org/ns/activitystreams',
id: flag_id,
type: 'Flag',
content: long_comment,
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: [
ActivityPub::TagManager.instance.uri_for(flagged),
ActivityPub::TagManager.instance.uri_for(status),
],
}.with_indifferent_access, sender)
end
let(:long_comment) { Faker::Lorem.characters(number: 6000) }
before do
subject.perform
end
it 'creates a report but with a truncated comment' do
report = Report.find_by(account: sender, target_account: flagged)
expect(report).to_not be_nil
expect(report.comment.length).to eq 5000
expect(report.comment).to eq long_comment[0...5000]
expect(report.status_ids).to eq [status.id]
end
end
context 'when the reported status is private and should not be visible to the remote server' do context 'when the reported status is private and should not be visible to the remote server' do
let(:status) { Fabricate(:status, account: flagged, uri: 'foobar', visibility: :private) } let(:status) { Fabricate(:status, account: flagged, uri: 'foobar', visibility: :private) }

View file

@ -121,10 +121,17 @@ describe Report do
end end
describe 'validations' do describe 'validations' do
it 'is invalid if comment is longer than 1000 characters' do let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') }
it 'is invalid if comment is longer than 1000 characters only if reporter is local' do
report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001)) report = Fabricate.build(:report, comment: Faker::Lorem.characters(number: 1001))
report.valid? expect(report.valid?).to be false
expect(report).to model_have_error_on_field(:comment) expect(report).to model_have_error_on_field(:comment)
end end
it 'is valid if comment is longer than 1000 characters and reporter is not local' do
report = Fabricate.build(:report, account: remote_account, comment: Faker::Lorem.characters(number: 1001))
expect(report.valid?).to be true
end
end end
end end

View file

@ -6,6 +6,14 @@ RSpec.describe ReportService, type: :service do
subject { described_class.new } subject { described_class.new }
let(:source_account) { Fabricate(:account) } let(:source_account) { Fabricate(:account) }
let(:target_account) { Fabricate(:account) }
context 'with a local account' do
it 'has a uri' do
report = subject.call(source_account, target_account)
expect(report.uri).to_not be_nil
end
end
context 'with a remote account' do context 'with a remote account' do
let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') } let(:remote_account) { Fabricate(:account, domain: 'example.com', protocol: :activitypub, inbox_url: 'http://example.com/inbox') }
@ -35,7 +43,6 @@ RSpec.describe ReportService, type: :service do
-> { described_class.new.call(source_account, target_account, status_ids: [status.id]) } -> { described_class.new.call(source_account, target_account, status_ids: [status.id]) }
end end
let(:target_account) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: target_account, visibility: :direct) } let(:status) { Fabricate(:status, account: target_account, visibility: :direct) }
context 'when it is addressed to the reporter' do context 'when it is addressed to the reporter' do
@ -91,7 +98,6 @@ RSpec.describe ReportService, type: :service do
-> { described_class.new.call(source_account, target_account) } -> { described_class.new.call(source_account, target_account) }
end end
let!(:target_account) { Fabricate(:account) }
let!(:other_report) { Fabricate(:report, target_account: target_account) } let!(:other_report) { Fabricate(:report, target_account: target_account) }
before do before do

185
yarn.lock
View file

@ -1055,14 +1055,6 @@
resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310" resolved "https://registry.yarnpkg.com/@babel/regjsgen/-/regjsgen-0.8.0.tgz#f0ba69b075e1f05fb2825b7fad991e7adbb18310"
integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA==
"@babel/runtime-corejs3@^7.10.2":
version "7.10.3"
resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.10.3.tgz#931ed6941d3954924a7aa967ee440e60c507b91a"
integrity sha512-HA7RPj5xvJxQl429r5Cxr2trJwOfPjKiqhCXcdQPSqO2G0RHPZpXu4fkYmBaTKCp2c/jRaMK9GB/lN+7zvvFPw==
dependencies:
core-js-pure "^3.0.0"
regenerator-runtime "^0.13.4"
"@babel/runtime@7.0.0": "@babel/runtime@7.0.0":
version "7.0.0" version "7.0.0"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.0.0.tgz#adeb78fedfc855aa05bc041640f3f6f98e85424c"
@ -1070,7 +1062,7 @@
dependencies: dependencies:
regenerator-runtime "^0.12.0" regenerator-runtime "^0.12.0"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": "@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.11.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.13.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.2.0", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.5", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.7.2", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2":
version "7.21.5" version "7.21.5"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.5.tgz#8492dddda9644ae3bda3b45eabe87382caee7200"
integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q== integrity sha512-8jI69toZqqcsnqGGqwGS4Qb1VwLOEp4hz+CXPywcvjs60u3B4Pom/U/7rm4W8tMOYEB+E9wgD0mW1l3r8qlI9Q==
@ -1822,18 +1814,18 @@
magic-string "^0.25.0" magic-string "^0.25.0"
string.prototype.matchall "^4.0.6" string.prototype.matchall "^4.0.6"
"@testing-library/dom@^8.0.0": "@testing-library/dom@^9.0.0":
version "8.1.0" version "9.2.0"
resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.1.0.tgz#f8358b1883844ea569ba76b7e94582168df5370d" resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-9.2.0.tgz#0e1f45e956f2a16f471559c06edd8827c4832f04"
integrity sha512-kmW9alndr19qd6DABzQ978zKQ+J65gU2Rzkl8hriIetPnwpesRaK4//jEQyYh8fEALmGhomD/LBQqt+o+DL95Q== integrity sha512-xTEnpUKiV/bMyEsE5bT4oYA0x0Z/colMtxzUY8bKyPXBNLn/e0V4ZjBZkEhms0xE4pv9QsPfSRu9AWS4y5wGvA==
dependencies: dependencies:
"@babel/code-frame" "^7.10.4" "@babel/code-frame" "^7.10.4"
"@babel/runtime" "^7.12.5" "@babel/runtime" "^7.12.5"
"@types/aria-query" "^4.2.0" "@types/aria-query" "^5.0.1"
aria-query "^4.2.2" aria-query "^5.0.0"
chalk "^4.1.0" chalk "^4.1.0"
dom-accessibility-api "^0.5.6" dom-accessibility-api "^0.5.9"
lz-string "^1.4.4" lz-string "^1.5.0"
pretty-format "^27.0.2" pretty-format "^27.0.2"
"@testing-library/jest-dom@^5.16.5": "@testing-library/jest-dom@^5.16.5":
@ -1851,14 +1843,14 @@
lodash "^4.17.15" lodash "^4.17.15"
redent "^3.0.0" redent "^3.0.0"
"@testing-library/react@^12.1.5": "@testing-library/react@^14.0.0":
version "12.1.5" version "14.0.0"
resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-14.0.0.tgz#59030392a6792450b9ab8e67aea5f3cc18d6347c"
integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg== integrity sha512-S04gSNJbYE30TlIMLTzv6QCTzt9AqIF5y6s6SzVFILNcNvbV/jU96GeiTPillGQo+Ny64M/5PV7klNYYgv5Dfg==
dependencies: dependencies:
"@babel/runtime" "^7.12.5" "@babel/runtime" "^7.12.5"
"@testing-library/dom" "^8.0.0" "@testing-library/dom" "^9.0.0"
"@types/react-dom" "<18.0.0" "@types/react-dom" "^18.0.0"
"@tootallnate/once@2": "@tootallnate/once@2":
version "2.0.0" version "2.0.0"
@ -1870,10 +1862,10 @@
resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad" resolved "https://registry.yarnpkg.com/@trysound/sax/-/sax-0.2.0.tgz#cccaab758af56761eb7bf37af6f03f326dd798ad"
integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA== integrity sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==
"@types/aria-query@^4.2.0": "@types/aria-query@^5.0.1":
version "4.2.0" version "5.0.1"
resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.0.tgz#14264692a9d6e2fa4db3df5e56e94b5e25647ac0" resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc"
integrity sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A== integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q==
"@types/babel__core@^7.1.12", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.3": "@types/babel__core@^7.1.12", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.3":
version "7.1.18" version "7.1.18"
@ -2184,29 +2176,17 @@
resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb" resolved "https://registry.yarnpkg.com/@types/qs/-/qs-6.9.7.tgz#63bb7d067db107cc1e457c303bc25d511febf6cb"
integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw== integrity sha512-FGa1F62FT09qcrueBA6qYTrJPVDzah9a+493+o2PCXsesWHIn27G98TsSMs3WPNbZIEj4+VJf6saSFpvD+3Zsw==
"@types/raf@^3.4.0":
version "3.4.0"
resolved "https://registry.yarnpkg.com/@types/raf/-/raf-3.4.0.tgz#2b72cbd55405e071f1c4d29992638e022b20acc2"
integrity sha512-taW5/WYqo36N7V39oYyHP9Ipfd5pNFvGTIQsNGj86xV88YQ7GnI30/yMfKDF7Zgin0m3e+ikX88FvImnK4RjGw==
"@types/range-parser@*": "@types/range-parser@*":
version "1.2.4" version "1.2.4"
resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc" resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.4.tgz#cd667bcfdd025213aafb7ca5915a932590acdcdc"
integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw== integrity sha512-EEhsLsD6UsDM1yFhAvy0Cjr6VwmpMWqFBCb9w07wVugF7w9nfajxLuVmngTIpgS6svCnm6Vaw+MZhoDCKnOfsw==
"@types/react-dom@<18.0.0": "@types/react-dom@^18.0.0", "@types/react-dom@^18.2.4":
version "17.0.15" version "18.2.4"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.15.tgz#f2c8efde11521a4b7991e076cb9c70ba3bb0d156" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.2.4.tgz#13f25bfbf4e404d26f62ac6e406591451acba9e0"
integrity sha512-Tr9VU9DvNoHDWlmecmcsE5ZZiUkYx+nKBzum4Oxe1K0yJVyBlfbq7H3eXjxXqJczBKqPGq3EgfTru4MgKb9+Yw== integrity sha512-G2mHoTMTL4yoydITgOGwWdWMVd8sNgyEP85xVmMKAPUBwQWm9wBPQUmvbeF4V3WBY1P7mmL4BkjQ0SqUpf1snw==
dependencies: dependencies:
"@types/react" "^17" "@types/react" "*"
"@types/react-dom@^16.9.18":
version "16.9.18"
resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-16.9.18.tgz#1fda8b84370b1339d639a797a84c16d5a195b419"
integrity sha512-lmNARUX3+rNF/nmoAFqasG0jAA7q6MeGZK/fdeLwY3kAA4NPgHHrG5bNQe2B5xmD4B+x6Z6h0rEJQ7MEEgQxsw==
dependencies:
"@types/react" "^16"
"@types/react-helmet@^6.1.6": "@types/react-helmet@^6.1.6":
version "6.1.6" version "6.1.6"
@ -2328,28 +2308,10 @@
dependencies: dependencies:
"@types/react" "*" "@types/react" "*"
"@types/react@*", "@types/react@^17": "@types/react@*", "@types/react@>=16.9.11", "@types/react@^18.0.26":
version "17.0.44" version "18.2.6"
resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.44.tgz#c3714bd34dd551ab20b8015d9d0dbec812a51ec7" resolved "https://registry.yarnpkg.com/@types/react/-/react-18.2.6.tgz#5cd53ee0d30ffc193b159d3516c8c8ad2f19d571"
integrity sha512-Ye0nlw09GeMp2Suh8qoOv0odfgCoowfM/9MG6WeRD60Gq9wS90bdkdRtYbRkNhXOpG4H+YXGvj4wOWhAC0LJ1g== integrity sha512-wRZClXn//zxCFW+ye/D2qY65UsYP1Fpex2YXorHc8awoNamkMZSvBxwxdYVInsHOZZd2Ppq8isnSzJL5Mpf8OA==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/react@>=16.9.11":
version "18.0.26"
resolved "https://registry.yarnpkg.com/@types/react/-/react-18.0.26.tgz#8ad59fc01fef8eaf5c74f4ea392621749f0b7917"
integrity sha512-hCR3PJQsAIXyxhTNSiDFY//LhnMZWpNNr5etoCqx/iUfGc5gXWtQR2Phl908jVR6uPXacojQWTg4qRpkxTuGug==
dependencies:
"@types/prop-types" "*"
"@types/scheduler" "*"
csstype "^3.0.2"
"@types/react@^16", "@types/react@^16.14.38":
version "16.14.38"
resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.38.tgz#b814d157ca8906603593d5106f6d733af9b79df4"
integrity sha512-PbEjuhwkdH6IB5Sak6BFAqpVMHY/wJxa0EG3bKkr0vWA2hSDIq3iEMhHyqjXrDFMqRzkiQkdyNXOnoELrh/9aQ==
dependencies: dependencies:
"@types/prop-types" "*" "@types/prop-types" "*"
"@types/scheduler" "*" "@types/scheduler" "*"
@ -2980,14 +2942,6 @@ argparse@^2.0.1:
resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38"
integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==
aria-query@^4.2.2:
version "4.2.2"
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-4.2.2.tgz#0d2ca6c9aceb56b8977e9fed6aed7e15bbd2f83b"
integrity sha512-o/HelwhuKpTj/frsOsbNLNgnNGVIFsVP/SW2BSF14gVl7kAfMOJ6/8wUAUvG1R1NHKrfG+2sHZTu0yauT1qBrA==
dependencies:
"@babel/runtime" "^7.10.2"
"@babel/runtime-corejs3" "^7.10.2"
aria-query@^5.0.0, aria-query@^5.1.3: aria-query@^5.0.0, aria-query@^5.1.3:
version "5.1.3" version "5.1.3"
resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e" resolved "https://registry.yarnpkg.com/aria-query/-/aria-query-5.1.3.tgz#19db27cd101152773631396f7a95a3b58c22c35e"
@ -4156,11 +4110,6 @@ core-js-compat@^3.25.1:
dependencies: dependencies:
browserslist "^4.21.4" browserslist "^4.21.4"
core-js-pure@^3.0.0:
version "3.6.5"
resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.6.5.tgz#c79e75f5e38dbc85a662d91eea52b8256d53b813"
integrity sha512-lacdXOimsiD0QyNf9BC/mxivNJ/ybBGJXQFKzRekp1WTHoVUWsUHEn+2T8GJAzzIhyOuXA+gOxCVN3l+5PLPUA==
core-js@^2.5.0: core-js@^2.5.0:
version "2.6.12" version "2.6.12"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec"
@ -4761,6 +4710,11 @@ dom-accessibility-api@^0.5.6:
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9" resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.6.tgz#3f5d43b52c7a3bd68b5fb63fa47b4e4c1fdf65a9"
integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw== integrity sha512-DplGLZd8L1lN64jlT27N9TVSESFR5STaEJvX+thCby7fuCHonfPpAlodYc3vuUYbDuDec5w8AMP7oCM5TWFsqw==
dom-accessibility-api@^0.5.9:
version "0.5.16"
resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453"
integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==
dom-helpers@^3.4.0: dom-helpers@^3.4.0:
version "3.4.0" version "3.4.0"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8" resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.4.0.tgz#e9b369700f959f62ecde5a6babde4bccd9169af8"
@ -7908,10 +7862,10 @@ lru-cache@^9.0.0:
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.0.1.tgz#ac061ed291f8b9adaca2b085534bb1d3b61bef83" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-9.0.1.tgz#ac061ed291f8b9adaca2b085534bb1d3b61bef83"
integrity sha512-C8QsKIN1UIXeOs3iWmiZ1lQY+EnKDojWd37fXy1aSbJvH4iSma1uy2OWuoB3m4SYRli5+CUjDv3Dij5DVoetmg== integrity sha512-C8QsKIN1UIXeOs3iWmiZ1lQY+EnKDojWd37fXy1aSbJvH4iSma1uy2OWuoB3m4SYRli5+CUjDv3Dij5DVoetmg==
lz-string@^1.4.4: lz-string@^1.5.0:
version "1.4.4" version "1.5.0"
resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941"
integrity sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY= integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==
magic-string@^0.25.0, magic-string@^0.25.7: magic-string@^0.25.0, magic-string@^0.25.7:
version "0.25.9" version "0.25.9"
@ -9502,7 +9456,7 @@ quick-lru@^4.0.1:
resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f" resolved "https://registry.yarnpkg.com/quick-lru/-/quick-lru-4.0.1.tgz#5b8878f113a58217848c6482026c73e1ba57727f"
integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g== integrity sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==
raf@^3.1.0, raf@^3.4.1: raf@^3.1.0:
version "3.4.1" version "3.4.1"
resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39" resolved "https://registry.yarnpkg.com/raf/-/raf-3.4.1.tgz#0742e99a4a6552f445d73e3ee0328af0ff1ede39"
integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA== integrity sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==
@ -9539,15 +9493,13 @@ raw-body@2.5.1:
iconv-lite "0.4.24" iconv-lite "0.4.24"
unpipe "1.0.0" unpipe "1.0.0"
react-dom@^16.14.0: react-dom@^18.2.0:
version "16.14.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.14.0.tgz#7ad838ec29a777fb3c75c3a190f661cf92ab8b89" resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-18.2.0.tgz#22aaf38708db2674ed9ada224ca4aa708d821e3d"
integrity sha512-1gCeQXDLoIqMgqD3IO2Ah9bnf0w9kzhwN5q4FGnHZ67hBm9yePzB5JJAIQCc8x3pFnNlwFq4RidZggNAAkzWWw== integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1" scheduler "^0.23.0"
prop-types "^15.6.2"
scheduler "^0.19.1"
react-event-listener@^0.6.0: react-event-listener@^0.6.0:
version "0.6.6" version "0.6.6"
@ -9617,7 +9569,12 @@ react-intl@^2.9.0:
intl-relativeformat "^2.1.0" intl-relativeformat "^2.1.0"
invariant "^2.1.1" invariant "^2.1.1"
react-is@^16.13.1, react-is@^16.7.0, react-is@^16.8.6: "react-is@^16.12.0 || ^17.0.0 || ^18.0.0", react-is@^18.2.0:
version "18.2.0"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-18.2.0.tgz#199431eeaaa2e09f86427efbb4f1473edb47609b"
integrity sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==
react-is@^16.13.1, react-is@^16.7.0:
version "16.13.1" version "16.13.1"
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4"
integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==
@ -9735,6 +9692,14 @@ react-select@*, react-select@^5.7.3:
react-transition-group "^4.3.0" react-transition-group "^4.3.0"
use-isomorphic-layout-effect "^1.1.2" use-isomorphic-layout-effect "^1.1.2"
react-shallow-renderer@^16.15.0:
version "16.15.0"
resolved "https://registry.yarnpkg.com/react-shallow-renderer/-/react-shallow-renderer-16.15.0.tgz#48fb2cf9b23d23cde96708fe5273a7d3446f4457"
integrity sha512-oScf2FqQ9LFVQgA73vr86xl2NaOIX73rh+YFqcOp68CWj56tSfgtGKrEbyhCj0rSijyG9M1CYprTh39fBi5hzA==
dependencies:
object-assign "^4.1.1"
react-is "^16.12.0 || ^17.0.0 || ^18.0.0"
react-side-effect@^2.1.0: react-side-effect@^2.1.0:
version "2.1.2" version "2.1.2"
resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a" resolved "https://registry.yarnpkg.com/react-side-effect/-/react-side-effect-2.1.2.tgz#dc6345b9e8f9906dc2eeb68700b615e0b4fe752a"
@ -9778,15 +9743,14 @@ react-swipeable-views@^0.14.0:
react-swipeable-views-utils "^0.14.0" react-swipeable-views-utils "^0.14.0"
warning "^4.0.1" warning "^4.0.1"
react-test-renderer@^16.14.0: react-test-renderer@^18.2.0:
version "16.14.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.14.0.tgz#e98360087348e260c56d4fe2315e970480c228ae" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-18.2.0.tgz#1dd912bd908ff26da5b9fca4fd1c489b9523d37e"
integrity sha512-L8yPjqPE5CZO6rKsKXRO/rVPiaCOy0tQQJbC+UjPNlobl5mad59lvPjwFsQHTvL03caVDIVr9x9/OSgDe6I5Eg== integrity sha512-JWD+aQ0lh2gvh4NM3bBM42Kx+XybOxCpgYK7F8ugAlpaTSnWsX+39Z4XkOykGZAHrjwwTZT3x3KxswVWxHPUqA==
dependencies: dependencies:
object-assign "^4.1.1" react-is "^18.2.0"
prop-types "^15.6.2" react-shallow-renderer "^16.15.0"
react-is "^16.8.6" scheduler "^0.23.0"
scheduler "^0.19.1"
react-textarea-autosize@*, react-textarea-autosize@^8.4.1: react-textarea-autosize@*, react-textarea-autosize@^8.4.1:
version "8.4.1" version "8.4.1"
@ -9814,14 +9778,12 @@ react-transition-group@^4.3.0:
loose-envify "^1.4.0" loose-envify "^1.4.0"
prop-types "^15.6.2" prop-types "^15.6.2"
react@^16.14.0: react@^18.2.0:
version "16.14.0" version "18.2.0"
resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" resolved "https://registry.yarnpkg.com/react/-/react-18.2.0.tgz#555bd98592883255fa00de14f1151a917b5d77d5"
integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1"
prop-types "^15.6.2"
read-pkg-up@^7.0.1: read-pkg-up@^7.0.1:
version "7.0.1" version "7.0.1"
@ -9951,7 +9913,7 @@ regenerator-runtime@^0.12.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz#fa1a71544764c036f8c49b13a08b2594c9f8a0de"
integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg== integrity sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==
regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.3, regenerator-runtime@^0.13.4: regenerator-runtime@^0.13.11, regenerator-runtime@^0.13.3:
version "0.13.11" version "0.13.11"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9"
integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==
@ -10301,13 +10263,12 @@ saxes@^6.0.0:
dependencies: dependencies:
xmlchars "^2.2.0" xmlchars "^2.2.0"
scheduler@^0.19.1: scheduler@^0.23.0:
version "0.19.1" version "0.23.0"
resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.19.1.tgz#4f3e2ed2c1a7d65681f4c854fa8c5a1ccb40f196" resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.23.0.tgz#ba8041afc3d30eb206a487b6b384002e4e61fdfe"
integrity sha512-n/zwRWRYSUj0/3g/otKDRPMh6qv2SYMWNq85IEa8iZyAv8od9zDYpGSnpBEjNgcMNq6Scbu5KfIPxNF72R/2EA== integrity sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==
dependencies: dependencies:
loose-envify "^1.1.0" loose-envify "^1.1.0"
object-assign "^4.1.1"
schema-utils@^1.0.0: schema-utils@^1.0.0:
version "1.0.0" version "1.0.0"