From 564efd06515edc524a8a1cdf7a3d8a7d9a376c04 Mon Sep 17 00:00:00 2001 From: Eugen Rochko Date: Mon, 14 Feb 2022 21:27:53 +0100 Subject: Add appeals (#17364) * Add appeals * Add ability to reject appeals and ability to browse pending appeals in admin UI * Add strikes to account page in settings * Various fixes and improvements - Add separate notification setting for appeals, separate from reports - Fix style of links in report/strike header - Change approving an appeal to not restore statuses (due to federation complexities) - Change style of successfully appealed strikes on account settings page - Change account settings page to only show unappealed or recently appealed strikes * Change appealed_at to overruled_at * Fix missing method error --- config/brakeman.ignore | 64 ++++++++++++++++++-------------- config/locales/en.yml | 77 +++++++++++++++++++++++++++++++++++++-- config/locales/simple_form.en.yml | 9 ++++- config/navigation.rb | 4 +- config/routes.rb | 15 ++++++++ config/settings.yml | 1 + 6 files changed, 136 insertions(+), 34 deletions(-) (limited to 'config') diff --git a/config/brakeman.ignore b/config/brakeman.ignore index 4245b7192..6ffe12ae0 100644 --- a/config/brakeman.ignore +++ b/config/brakeman.ignore @@ -1,25 +1,5 @@ { "ignored_warnings": [ - { - "warning_type": "SQL Injection", - "warning_code": 0, - "fingerprint": "04dbbc249b989db2e0119bbb0f59c9818e12889d2b97c529cdc0b1526002ba4b", - "check_name": "SQL", - "message": "Possible SQL injection", - "file": "app/models/report.rb", - "line": 113, - "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/", - "code": "Admin::ActionLog.from(\"(#{[Admin::ActionLog.where(:target_type => \"Report\", :target_id => id, :created_at => ((created_at..updated_at))).unscope(:order), Admin::ActionLog.where(:target_type => \"Account\", :target_id => target_account_id, :created_at => ((created_at..updated_at))).unscope(:order), Admin::ActionLog.where(:target_type => \"Status\", :target_id => status_ids, :created_at => ((created_at..updated_at))).unscope(:order)].map do\n \"(#{query.to_sql})\"\n end.join(\" UNION ALL \")}) AS admin_action_logs\")", - "render_path": null, - "location": { - "type": "method", - "class": "Report", - "method": "history" - }, - "user_input": "Admin::ActionLog.where(:target_type => \"Status\", :target_id => status_ids, :created_at => ((created_at..updated_at))).unscope(:order)", - "confidence": "High", - "note": "" - }, { "warning_type": "SQL Injection", "warning_code": 0, @@ -27,7 +7,7 @@ "check_name": "SQL", "message": "Possible SQL injection", "file": "app/models/status.rb", - "line": 100, + "line": 104, "link": "https://brakemanscanner.org/docs/warning_types/sql_injection/", "code": "result.joins(\"INNER JOIN statuses_tags t#{id} ON t#{id}.status_id = statuses.id AND t#{id}.tag_id = #{id}\")", "render_path": null, @@ -107,7 +87,7 @@ "check_name": "PermitAttributes", "message": "Potentially dangerous key allowed for mass assignment", "file": "app/controllers/api/v1/admin/reports_controller.rb", - "line": 78, + "line": 90, "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/", "code": "params.permit(:resolved, :account_id, :target_account_id)", "render_path": null, @@ -140,6 +120,36 @@ "confidence": "Medium", "note": "" }, + { + "warning_type": "Cross-Site Scripting", + "warning_code": 2, + "fingerprint": "afad51718ae373b2f19d2513029fd2afccf58b9148e475934bc6a162ee33c352", + "check_name": "CrossSiteScripting", + "message": "Unescaped model attribute", + "file": "app/views/admin/disputes/appeals/_appeal.html.haml", + "line": 7, + "link": "https://brakemanscanner.org/docs/warning_types/cross_site_scripting", + "code": "t((Unresolved Model).new.strike.action, :scope => \"admin.strikes.actions\", :name => content_tag(:span, (Unresolved Model).new.strike.account.username, :class => \"username\"), :target => content_tag(:span, (Unresolved Model).new.account.acct, :class => \"target\"))", + "render_path": [ + { + "type": "template", + "name": "admin/disputes/appeals/index", + "line": 16, + "file": "app/views/admin/disputes/appeals/index.html.haml", + "rendered": { + "name": "admin/disputes/appeals/_appeal", + "file": "app/views/admin/disputes/appeals/_appeal.html.haml" + } + } + ], + "location": { + "type": "template", + "template": "admin/disputes/appeals/_appeal" + }, + "user_input": "(Unresolved Model).new.strike", + "confidence": "Weak", + "note": "" + }, { "warning_type": "Redirect", "warning_code": 18, @@ -194,7 +204,7 @@ { "type": "template", "name": "admin/trends/links/index", - "line": 37, + "line": 39, "file": "app/views/admin/trends/links/index.html.haml", "rendered": { "name": "admin/trends/links/_preview_card", @@ -213,13 +223,13 @@ { "warning_type": "Mass Assignment", "warning_code": 105, - "fingerprint": "e867661b2c9812bc8b75a5df12b28e2a53ab97015de0638b4e732fe442561b28", + "fingerprint": "f9de0ca4b04ae4b51b74d98db14dcbb6dae6809e627b58e711019cf9b4a47866", "check_name": "PermitAttributes", "message": "Potentially dangerous key allowed for mass assignment", "file": "app/controllers/api/v1/reports_controller.rb", "line": 36, "link": "https://brakemanscanner.org/docs/warning_types/mass_assignment/", - "code": "params.permit(:account_id, :comment, :forward, :status_ids => ([]))", + "code": "params.permit(:account_id, :comment, :category, :forward, :status_ids => ([]), :rule_ids => ([]))", "render_path": null, "location": { "type": "method", @@ -231,6 +241,6 @@ "note": "" } ], - "updated": "2021-11-14 05:26:09 +0100", - "brakeman_version": "5.1.2" + "updated": "2022-02-13 02:24:12 +0100", + "brakeman_version": "5.2.1" } diff --git a/config/locales/en.yml b/config/locales/en.yml index 1809f123e..05c64e18a 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -94,7 +94,6 @@ en: account_moderation_notes: create: Leave note created_msg: Moderation note successfully created! - delete: Delete destroyed_msg: Moderation note successfully destroyed! accounts: add_email_domain_block: Block e-mail domain @@ -163,6 +162,11 @@ en: not_subscribed: Not subscribed pending: Pending review perform_full_suspension: Suspend + previous_strikes: Previous strikes + previous_strikes_description_html: + one: This account has one strike. + other: This account has %{count} strikes. + zero: This account is in good standing. promote: Promote protocol: Protocol public: Public @@ -227,6 +231,7 @@ en: whitelisted: Allowed for federation action_logs: action_types: + approve_appeal: Approve Appeal approve_user: Approve User assigned_to_self_report: Assign Report change_email_user: Change E-mail for User @@ -258,6 +263,7 @@ en: enable_user: Enable User memorialize_account: Memorialize Account promote_user: Promote User + reject_appeal: Reject Appeal reject_user: Reject User remove_avatar_user: Remove Avatar reopen_report: Reopen Report @@ -276,6 +282,7 @@ en: update_domain_block: Update Domain Block update_status: Update Post actions: + approve_appeal_html: "%{name} approved moderation decision appeal from %{target}" approve_user_html: "%{name} approved sign-up from %{target}" assigned_to_self_report_html: "%{name} assigned report %{target} to themselves" change_email_user_html: "%{name} changed the e-mail address of user %{target}" @@ -307,6 +314,7 @@ en: enable_user_html: "%{name} enabled login for user %{target}" memorialize_account_html: "%{name} turned %{target}'s account into a memoriam page" promote_user_html: "%{name} promoted user %{target}" + reject_appeal_html: "%{name} rejected moderation decision appeal from %{target}" reject_user_html: "%{name} rejected sign-up from %{target}" remove_avatar_user_html: "%{name} removed %{target}'s avatar" reopen_report_html: "%{name} reopened report %{target}" @@ -385,6 +393,9 @@ en: media_storage: Media storage new_users: new users opened_reports: reports opened + pending_appeals_html: + one: "1 pending appeal" + other: "%{count} pending appeals" pending_reports_html: one: "1 pending report" other: "%{count} pending reports" @@ -402,6 +413,10 @@ en: top_languages: Top active languages top_servers: Top active servers website: Website + disputes: + appeals: + empty: No appeals found. + title: Appeals domain_allows: add_new: Allow federation with domain created_msg: Domain has been successfully allowed for federation @@ -720,6 +735,16 @@ en: no_status_selected: No posts were changed as none were selected title: Account posts with_media: With media + strikes: + actions: + delete_statuses: "%{name} deleted %{target}'s posts" + disable: "%{name} froze %{target}'s account" + none: "%{name} sent a warning to %{target}" + sensitive: "%{name} marked %{target}'s account as sensitive" + silence: "%{name} limited %{target}'s account" + suspend: "%{name} suspended %{target}'s account" + appeal_approved: Appealed + appeal_pending: Appeal pending system_checks: database_schema_check: message_html: There are pending database migrations. Please run them to ensure the application behaves as expected @@ -781,6 +806,17 @@ en: empty: You haven't defined any warning presets yet. title: Manage warning presets admin_mailer: + new_appeal: + actions: + delete_statuses: to delete their posts + disable: to freeze their account + none: a warning + sensitive: to mark their account as sensitive + silence: to limit their account + suspend: to suspend their account + body: "%{target} is appealing a moderation decision by %{action_taken_by} from %{date}, which was %{type}. They wrote:" + next_steps: You can approve the appeal to undo the moderation decision, or ignore it. + subject: "%{username} is appealing a moderation decision on %{instance}" new_pending_account: body: The details of the new account are below. You can approve or reject this application. subject: New account up for review on %{instance} (%{username}) @@ -871,7 +907,6 @@ en: status: account_status: Account status confirming: Waiting for e-mail confirmation to be completed. - functional: Your account is fully operational. pending: Your application is pending review by our staff. This may take some time. You will receive an e-mail if your application is approved. redirecting_to: Your account is inactive because it is currently redirecting to %{acct}. too_fast: Form submitted too fast, try again. @@ -937,6 +972,32 @@ en: directory: Profile directory explanation: Discover users based on their interests explore_mastodon: Explore %{title} + disputes: + strikes: + action_taken: Action taken + appeal: Appeal + appeal_approved: This strike has been successfully appealed and is no longer valid + appeal_rejected: The appeal has been rejected + appeal_submitted_at: Appeal submitted + appealed_msg: Your appeal has been submitted. If it is approved, you will be notified. + appeals: + submit: Submit appeal + associated_report: Associated report + created_at: Dated + recipient: Addressed to + status: 'Post #%{id}' + status_removed: Post already removed from system + title: "%{action} from %{date}" + title_actions: + delete_statuses: Post removal + disable: Freezing of account + none: Warning + sensitive: Marking as sensitive of account + silence: Limitation of account + suspend: Suspension of account + your_appeal_approved: Your appeal has been approved + your_appeal_pending: You have submitted an appeal + your_appeal_rejected: Your appeal has been rejected domain_validator: invalid_domain: is not a valid domain name errors: @@ -1501,6 +1562,15 @@ en: recovery_instructions_html: If you ever lose access to your phone, you can use one of the recovery codes below to regain access to your account. Keep the recovery codes safe. For example, you may print them and store them with other important documents. webauthn: Security keys user_mailer: + appeal_approved: + action: Go to your account + explanation: The appeal of the strike against your account on %{strike_date} that you submitted on %{appeal_date} has been approved. Your account is once again in good standing. + subject: Your appeal from %{date} has been approved + title: Appeal approved + appeal_rejected: + explanation: The appeal of the strike against your account on %{strike_date} that you submitted on %{appeal_date} has been rejected. + subject: Your appeal from %{date} has been rejected + title: Appeal rejected backup_ready: explanation: You requested a full backup of your Mastodon account. It's now ready for download! subject: Your archive is ready for download @@ -1512,6 +1582,8 @@ en: subject: Please confirm attempted sign in title: Sign in attempt warning: + appeal: Submit an appeal + appeal_description: If you believe this is an error, you can submit an appeal to the staff of %{instance}. categories: spam: Spam violation: Content violates the following community guidelines @@ -1523,7 +1595,6 @@ en: suspend: You can no longer use your account, and your profile and other data are no longer accessible. You can still login to request a backup of your data until the data is fully removed in about 30 days, but we will retain some basic data to prevent you from evading the suspension. get_in_touch: If you believe this is an error, you can reply to this e-mail to get in touch with the staff of %{instance}. reason: 'Reason:' - review_server_policies: Review server policies statuses: 'Posts that have been found in violation:' subject: delete_statuses: Your posts on %{acct} have been removed diff --git a/config/locales/simple_form.en.yml b/config/locales/simple_form.en.yml index d6376782d..03eefd0d5 100644 --- a/config/locales/simple_form.en.yml +++ b/config/locales/simple_form.en.yml @@ -27,6 +27,8 @@ en: scheduled_at: Leave blank to publish the announcement immediately starts_at: Optional. In case your announcement is bound to a specific time range text: You can use post syntax. Please be mindful of the space the announcement will take up on the user's screen + appeal: + text: You can only appeal a strike once defaults: autofollow: People who sign up through the invite will automatically follow you avatar: PNG, GIF or JPG. At most %{size}. Will be downscaled to %{dimensions}px @@ -119,6 +121,8 @@ en: scheduled_at: Schedule publication starts_at: Start of event text: Announcement + appeal: + text: Explain why this decision should be reversed defaults: autofollow: Invite to follow your account avatar: Avatar @@ -197,6 +201,7 @@ en: sign_up_requires_approval: Limit sign-ups severity: Rule notification_emails: + appeal: Someone appeals a moderator decision digest: Send digest e-mails favourite: Someone favourited your post follow: Someone followed you @@ -204,8 +209,8 @@ en: mention: Someone mentioned you pending_account: New account needs review reblog: Someone boosted your post - report: A new report is submitted - trending_tag: A new trend requires approval + report: New report is submitted + trending_tag: New trend requires review rule: text: Rule tag: diff --git a/config/navigation.rb b/config/navigation.rb index fc03a2a77..3fc3747d5 100644 --- a/config/navigation.rb +++ b/config/navigation.rb @@ -20,7 +20,7 @@ SimpleNavigation::Configuration.run do |navigation| n.item :statuses_cleanup, safe_join([fa_icon('history fw'), t('settings.statuses_cleanup')]), statuses_cleanup_url, if: -> { current_user.functional? } n.item :security, safe_join([fa_icon('lock fw'), t('settings.account')]), edit_user_registration_url do |s| - s.item :password, safe_join([fa_icon('lock fw'), t('settings.account_settings')]), edit_user_registration_url, highlights_on: %r{/auth/edit|/settings/delete|/settings/migration|/settings/aliases|/settings/login_activities} + s.item :password, safe_join([fa_icon('lock fw'), t('settings.account_settings')]), edit_user_registration_url, highlights_on: %r{/auth/edit|/settings/delete|/settings/migration|/settings/aliases|/settings/login_activities|^/disputes} s.item :two_factor_authentication, safe_join([fa_icon('mobile fw'), t('settings.two_factor_authentication')]), settings_two_factor_authentication_methods_url, highlights_on: %r{/settings/two_factor_authentication|/settings/otp_authentication|/settings/security_keys} s.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url end @@ -41,7 +41,7 @@ SimpleNavigation::Configuration.run do |navigation| n.item :moderation, safe_join([fa_icon('gavel fw'), t('moderation.title')]), admin_reports_url, if: proc { current_user.staff? } do |s| s.item :action_logs, safe_join([fa_icon('bars fw'), t('admin.action_logs.title')]), admin_action_logs_url s.item :reports, safe_join([fa_icon('flag fw'), t('admin.reports.title')]), admin_reports_url, highlights_on: %r{/admin/reports} - s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url(origin: 'local'), highlights_on: %r{/admin/accounts|/admin/pending_accounts} + s.item :accounts, safe_join([fa_icon('users fw'), t('admin.accounts.title')]), admin_accounts_url(origin: 'local'), highlights_on: %r{/admin/accounts|/admin/pending_accounts|/admin/disputes} s.item :invites, safe_join([fa_icon('user-plus fw'), t('admin.invites.title')]), admin_invites_path s.item :follow_recommendations, safe_join([fa_icon('user-plus fw'), t('admin.follow_recommendations.title')]), admin_follow_recommendations_path, highlights_on: %r{/admin/follow_recommendations} s.item :instances, safe_join([fa_icon('cloud fw'), t('admin.instances.title')]), admin_instances_url(limited: whitelist_mode? ? nil : '1'), highlights_on: %r{/admin/instances|/admin/domain_blocks|/admin/domain_allows}, if: -> { current_user.admin? } diff --git a/config/routes.rb b/config/routes.rb index 5edb36519..f59f2a5bb 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -164,6 +164,12 @@ Rails.application.routes.draw do resources :login_activities, only: [:index] end + namespace :disputes do + resources :strikes, only: [:show] do + resource :appeal, only: [:create] + end + end + resources :media, only: [:show] do get :player end @@ -324,6 +330,15 @@ Rails.application.routes.draw do end end end + + namespace :disputes do + resources :appeals, only: [:index] do + member do + post :approve + post :reject + end + end + end end get '/admin', to: redirect('/admin/dashboard', status: 302) diff --git a/config/settings.yml b/config/settings.yml index 06cee2532..e63788ba2 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -48,6 +48,7 @@ defaults: &defaults report: true pending_account: true trending_tag: true + appeal: true interactions: must_be_follower: false must_be_following: false -- cgit From 1abf0f90000c86bfbc5d6ac9a976834dcd17983a Mon Sep 17 00:00:00 2001 From: Claire Date: Wed, 16 Feb 2022 14:57:57 +0100 Subject: Fix 0 pluralization for some localization strings (#17576) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Apparently, when the `zero` case is not defined, the translation string used for `0` depends on the language. In French, `one` is used, causing some strings with hardcoded “one” or `1` to misrepresent the actual data. For instance, the dashboard would display « 1 utilisateur·rice en attente » for both 0 and 1 pending users. --- config/locales/en.yml | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'config') diff --git a/config/locales/en.yml b/config/locales/en.yml index 05c64e18a..cf4c2cc37 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -394,16 +394,16 @@ en: new_users: new users opened_reports: reports opened pending_appeals_html: - one: "1 pending appeal" + one: "%{count} pending appeal" other: "%{count} pending appeals" pending_reports_html: - one: "1 pending report" + one: "%{count} pending report" other: "%{count} pending reports" pending_tags_html: - one: "1 pending hashtag" + one: "%{count} pending hashtag" other: "%{count} pending hashtags" pending_users_html: - one: "1 pending user" + one: "%{count} pending user" other: "%{count} pending users" resolved_reports: reports resolved software: Software @@ -457,6 +457,7 @@ en: affected_accounts: one: One account in the database affected other: "%{count} accounts in the database affected" + zero: No account in the database is affected retroactive: silence: Undo limit of existing affected accounts from this domain suspend: Unsuspend existing affected accounts from this domain @@ -510,6 +511,7 @@ en: known_accounts: one: "%{count} known account" other: "%{count} known accounts" + zero: No known account moderation: all: All limited: Limited @@ -769,6 +771,7 @@ en: shared_by_over_week: one: Shared by one person over the last week other: Shared by %{count} people over the last week + zero: Shared by noone over the last week title: Trending links usage_comparison: Shared %{today} times today, compared to %{yesterday} yesterday pending_review: Pending review @@ -798,6 +801,7 @@ en: used_by_over_week: one: Used by one person over the last week other: Used by %{count} people over the last week + zero: Used by noone over the last week title: Trends warning_presets: add_new: Add new -- cgit