Browse Source

Fix link verification for remote accounts (#8868)

pull/4/head
Eugen Rochko 5 years ago
committed by GitHub
parent
commit
7fe137d2f7
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 108 additions and 65 deletions
  1. +23
    -3
      app/models/account.rb
  2. +1
    -5
      app/serializers/rest/account_serializer.rb
  3. +1
    -1
      app/services/verify_link_service.rb
  4. +83
    -56
      spec/services/verify_link_service_spec.rb

+ 23
- 3
app/models/account.rb View File

@ -312,8 +312,8 @@ class Account < ApplicationRecord
def initialize(account, attributes) def initialize(account, attributes)
@account = account @account = account
@attributes = attributes @attributes = attributes
@name = attributes['name'].strip[0, 255]
@value = attributes['value'].strip[0, 255]
@name = attributes['name'].strip[0, string_limit]
@value = attributes['value'].strip[0, string_limit]
@verified_at = attributes['verified_at']&.to_datetime @verified_at = attributes['verified_at']&.to_datetime
@errors = {} @errors = {}
end end
@ -322,8 +322,18 @@ class Account < ApplicationRecord
verified_at.present? verified_at.present?
end end
def value_for_verification
@value_for_verification ||= begin
if account.local?
value
else
ActionController::Base.helpers.strip_tags(value)
end
end
end
def verifiable? def verifiable?
value.present? && value.start_with?('http://', 'https://')
value_for_verification.present? && value_for_verification.start_with?('http://', 'https://')
end end
def mark_verified! def mark_verified!
@ -334,6 +344,16 @@ class Account < ApplicationRecord
def to_h def to_h
{ name: @name, value: @value, verified_at: @verified_at } { name: @name, value: @value, verified_at: @verified_at }
end end
private
def string_limit
if account.local?
255
else
2047
end
end
end end
class << self class << self

+ 1
- 5
app/serializers/rest/account_serializer.rb View File

@ -11,11 +11,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
has_many :emojis, serializer: REST::CustomEmojiSerializer has_many :emojis, serializer: REST::CustomEmojiSerializer
class FieldSerializer < ActiveModel::Serializer class FieldSerializer < ActiveModel::Serializer
attributes :name, :value
attribute :verified_at, if: :verifiable?
delegate :verifiable?, to: :object
attributes :name, :value, :verified_at
def value def value
Formatter.instance.format_field(object.account, object.value) Formatter.instance.format_field(object.account, object.value)

+ 1
- 1
app/services/verify_link_service.rb View File

@ -3,7 +3,7 @@
class VerifyLinkService < BaseService class VerifyLinkService < BaseService
def call(field) def call(field)
@link_back = ActivityPub::TagManager.instance.url_for(field.account) @link_back = ActivityPub::TagManager.instance.url_for(field.account)
@url = field.value
@url = field.value_for_verification
perform_request! perform_request!

+ 83
- 56
spec/services/verify_link_service_spec.rb View File

@ -3,80 +3,107 @@ require 'rails_helper'
RSpec.describe VerifyLinkService, type: :service do RSpec.describe VerifyLinkService, type: :service do
subject { described_class.new } subject { described_class.new }
let(:account) { Fabricate(:account, username: 'alice') }
let(:field) { Account::Field.new(account, 'name' => 'Website', 'value' => 'http://example.com') }
context 'given a local account' do
let(:account) { Fabricate(:account, username: 'alice') }
let(:field) { Account::Field.new(account, 'name' => 'Website', 'value' => 'http://example.com') }
before do
stub_request(:head, 'https://redirect.me/abc').to_return(status: 301, headers: { 'Location' => ActivityPub::TagManager.instance.url_for(account) })
stub_request(:get, 'http://example.com').to_return(status: 200, body: html)
subject.call(field)
end
context 'when a link contains an <a> back' do
let(:html) do
<<-HTML
<!doctype html>
<body>
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="me">Follow me on Mastodon</a>
</body>
HTML
before do
stub_request(:head, 'https://redirect.me/abc').to_return(status: 301, headers: { 'Location' => ActivityPub::TagManager.instance.url_for(account) })
stub_request(:get, 'http://example.com').to_return(status: 200, body: html)
subject.call(field)
end end
it 'marks the field as verified' do
expect(field.verified?).to be true
context 'when a link contains an <a> back' do
let(:html) do
<<-HTML
<!doctype html>
<body>
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="me">Follow me on Mastodon</a>
</body>
HTML
end
it 'marks the field as verified' do
expect(field.verified?).to be true
end
end end
end
context 'when a link contains an <a rel="noopener"> back' do
let(:html) do
<<-HTML
<!doctype html>
<body>
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="noopener me" target="_blank">Follow me on Mastodon</a>
</body>
HTML
context 'when a link contains an <a rel="noopener"> back' do
let(:html) do
<<-HTML
<!doctype html>
<body>
<a href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="noopener me" target="_blank">Follow me on Mastodon</a>
</body>
HTML
end
it 'marks the field as verified' do
expect(field.verified?).to be true
end
end end
it 'marks the field as verified' do
expect(field.verified?).to be true
context 'when a link contains a <link> back' do
let(:html) do
<<-HTML
<!doctype html>
<head>
<link type="text/html" href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="me" />
</head>
HTML
end
it 'marks the field as verified' do
expect(field.verified?).to be true
end
end end
end
context 'when a link contains a <link> back' do
let(:html) do
<<-HTML
<!doctype html>
<head>
<link type="text/html" href="#{ActivityPub::TagManager.instance.url_for(account)}" rel="me" />
</head>
HTML
context 'when a link goes through a redirect back' do
let(:html) do
<<-HTML
<!doctype html>
<head>
<link type="text/html" href="https://redirect.me/abc" rel="me" />
</head>
HTML
end
it 'marks the field as verified' do
expect(field.verified?).to be true
end
end end
it 'marks the field as verified' do
expect(field.verified?).to be true
context 'when a link does not contain a link back' do
let(:html) { '' }
it 'marks the field as verified' do
expect(field.verified?).to be false
end
end end
end end
context 'when a link goes through a redirect back' do
let(:html) do
<<-HTML
<!doctype html>
<head>
<link type="text/html" href="https://redirect.me/abc" rel="me" />
</head>
HTML
end
context 'given a remote account' do
let(:account) { Fabricate(:account, username: 'alice', domain: 'example.com', url: 'https://profile.example.com/alice') }
let(:field) { Account::Field.new(account, 'name' => 'Website', 'value' => '<a href="http://example.com" rel="me"><span class="invisible">http://</span><span class="">example.com</span><span class="invisible"></span></a>') }
it 'marks the field as verified' do
expect(field.verified?).to be true
before do
stub_request(:get, 'http://example.com').to_return(status: 200, body: html)
subject.call(field)
end end
end
context 'when a link does not contain a link back' do
let(:html) { '' }
context 'when a link contains an <a> back' do
let(:html) do
<<-HTML
<!doctype html>
<body>
<a href="https://profile.example.com/alice" rel="me">Follow me on Mastodon</a>
</body>
HTML
end
it 'marks the field as verified' do
expect(field.verified?).to be false
it 'marks the field as verified' do
expect(field.verified?).to be true
end
end end
end end
end end

Loading…
Cancel
Save