diff --git a/app/controllers/redirects_controller.rb b/app/controllers/redirects_controller.rb new file mode 100644 index 0000000000..fca80a3038 --- /dev/null +++ b/app/controllers/redirects_controller.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +class RedirectsController < ApplicationController + before_action :set_url + before_action :set_resource + + def show + expires_in(1.day, public: true, stale_while_revalidate: 30.seconds, stale_if_error: 1.day) unless user_signed_in? + + case @resource + when Account + redirect_to web_url("@#{@resource.pretty_acct}") + when Status + redirect_to web_url("@#{@resource.account.pretty_acct}/#{@resource.id}") + else + redirect_to @url, allow_other_host: true + end + end + + private + + def set_url + @url = params.require(:url) + end + + def set_resource + @resource = ResolveURLService.new.call(@url) if user_signed_in? + end +end diff --git a/app/javascript/mastodon/components/status_content.jsx b/app/javascript/mastodon/components/status_content.jsx index 82135b85ca..1b9cd7ed73 100644 --- a/app/javascript/mastodon/components/status_content.jsx +++ b/app/javascript/mastodon/components/status_content.jsx @@ -124,6 +124,7 @@ class StatusContent extends PureComponent { link.setAttribute('href', `/tags/${link.text.replace(/^#/, '')}`); } else { link.setAttribute('title', link.href); + link.setAttribute('href', `/redirect?url=${encodeURIComponent(link.href)}`); link.classList.add('unhandled-link'); } } diff --git a/app/javascript/mastodon/features/account/components/header.jsx b/app/javascript/mastodon/features/account/components/header.jsx index b10ef6ef76..e3a960a6b6 100644 --- a/app/javascript/mastodon/features/account/components/header.jsx +++ b/app/javascript/mastodon/features/account/components/header.jsx @@ -235,10 +235,18 @@ class Header extends ImmutablePureComponent { for (var i = 0; i < links.length; ++i) { link = links[i]; + if (link.classList.contains('status-link')) { + continue; + } + + link.classList.add('status-link'); + if (link.textContent[0] === '#' || (link.previousSibling && link.previousSibling.textContent && link.previousSibling.textContent[link.previousSibling.textContent.length - 1] === '#')) { link.addEventListener('click', this.handleHashtagClick, false); } else if (link.classList.contains('mention')) { link.addEventListener('click', this.handleMentionClick, false); + } else { + link.setAttribute('href', `/redirect?url=${encodeURIComponent(link.href)}`); } } } diff --git a/app/javascript/mastodon/features/status/components/card.jsx b/app/javascript/mastodon/features/status/components/card.jsx index f0ae40cbc4..6bf9becd3b 100644 --- a/app/javascript/mastodon/features/status/components/card.jsx +++ b/app/javascript/mastodon/features/status/components/card.jsx @@ -208,7 +208,7 @@ export default class Card extends PureComponent {
- +
) : spoilerButton} @@ -219,7 +219,7 @@ export default class Card extends PureComponent { return (
{embed} - {description} + {description}
); } else if (card.get('image')) { @@ -239,7 +239,7 @@ export default class Card extends PureComponent { return ( <> - + {embed} {description} diff --git a/config/brakeman.ignore b/config/brakeman.ignore new file mode 100644 index 0000000000..6b732da5c5 --- /dev/null +++ b/config/brakeman.ignore @@ -0,0 +1,29 @@ +{ + "ignored_warnings": [ + { + "warning_type": "Redirect", + "warning_code": 18, + "fingerprint": "8b543fdb2adb90f9c9e26ab6a67065ed577be23bc7fa3ed8158bb022394d0382", + "check_name": "Redirect", + "message": "Possible unprotected redirect", + "file": "app/controllers/redirects_controller.rb", + "line": 16, + "link": "https://brakemanscanner.org/docs/warning_types/redirect/", + "code": "redirect_to(params.require(:url), :allow_other_host => true)", + "render_path": null, + "location": { + "type": "method", + "class": "RedirectsController", + "method": "show" + }, + "user_input": "params.require(:url)", + "confidence": "Weak", + "cwe_id": [ + 601 + ], + "note": "" + } + ], + "updated": "2023-10-30 04:08:29 +0100", + "brakeman_version": "6.0.1" +} diff --git a/config/routes.rb b/config/routes.rb index f4662dd5da..a6e5711225 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -202,6 +202,7 @@ Rails.application.routes.draw do get '/media_proxy/:id/(*any)', to: 'media_proxy#show', as: :media_proxy, format: false get '/backups/:id/download', to: 'backups#download', as: :download_backup, format: false + get '/redirect', to: 'redirects#show', format: false resource :authorize_interaction, only: [:show] resource :share, only: [:show]