Merge tag 'v2.7.2' into instance_only_statuses

This commit is contained in:
Renato "Lond" Cerqueira
2019-02-19 21:07:43 +01:00
76 changed files with 1440 additions and 577 deletions

View File

@ -1,7 +1,7 @@
require 'rails_helper'
RSpec.describe ActivityPub::Activity::Announce do
let(:sender) { Fabricate(:account) }
let(:sender) { Fabricate(:account, followers_url: 'http://example.com/followers', uri: 'https://example.com/actor') }
let(:recipient) { Fabricate(:account) }
let(:status) { Fabricate(:status, account: recipient) }
@ -10,20 +10,162 @@ RSpec.describe ActivityPub::Activity::Announce do
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'foo',
type: 'Announce',
actor: ActivityPub::TagManager.instance.uri_for(sender),
object: ActivityPub::TagManager.instance.uri_for(status),
actor: 'https://example.com/actor',
object: object_json,
}.with_indifferent_access
end
describe '#perform' do
subject { described_class.new(json, sender) }
let(:unknown_object_json) do
{
'@context': 'https://www.w3.org/ns/activitystreams',
id: 'https://example.com/actor/hello-world',
type: 'Note',
attributedTo: 'https://example.com/actor',
content: 'Hello world',
to: 'http://example.com/followers',
}
end
before do
subject.perform
subject { described_class.new(json, sender) }
describe '#perform' do
context 'when sender is followed by a local account' do
before do
Fabricate(:account).follow!(sender)
stub_request(:get, 'https://example.com/actor/hello-world').to_return(body: Oj.dump(unknown_object_json))
subject.perform
end
context 'a known status' do
let(:object_json) do
ActivityPub::TagManager.instance.uri_for(status)
end
it 'creates a reblog by sender of status' do
expect(sender.reblogged?(status)).to be true
end
end
context 'an unknown status' do
let(:object_json) { 'https://example.com/actor/hello-world' }
it 'creates a reblog by sender of status' do
reblog = sender.statuses.first
expect(reblog).to_not be_nil
expect(reblog.reblog.text).to eq 'Hello world'
end
end
context 'self-boost of a previously unknown status with missing attributedTo' do
let(:object_json) do
{
id: 'https://example.com/actor#bar',
type: 'Note',
content: 'Lorem ipsum',
to: 'http://example.com/followers',
}
end
it 'creates a reblog by sender of status' do
expect(sender.reblogged?(sender.statuses.first)).to be true
end
end
context 'self-boost of a previously unknown status with correct attributedTo' do
let(:object_json) do
{
id: 'https://example.com/actor#bar',
type: 'Note',
content: 'Lorem ipsum',
attributedTo: 'https://example.com/actor',
to: 'http://example.com/followers',
}
end
it 'creates a reblog by sender of status' do
expect(sender.reblogged?(sender.statuses.first)).to be true
end
end
end
it 'creates a reblog by sender of status' do
expect(sender.reblogged?(status)).to be true
context 'when the status belongs to a local user' do
before do
subject.perform
end
let(:object_json) do
ActivityPub::TagManager.instance.uri_for(status)
end
it 'creates a reblog by sender of status' do
expect(sender.reblogged?(status)).to be true
end
end
context 'when the sender is relayed' do
let!(:relay_account) { Fabricate(:account, inbox_url: 'https://relay.example.com/inbox') }
let!(:relay) { Fabricate(:relay, inbox_url: 'https://relay.example.com/inbox') }
subject { described_class.new(json, sender, relayed_through_account: relay_account) }
context 'and the relay is enabled' do
before do
relay.update(state: :accepted)
subject.perform
end
let(:object_json) do
{
id: 'https://example.com/actor#bar',
type: 'Note',
content: 'Lorem ipsum',
to: 'http://example.com/followers',
}
end
it 'creates a reblog by sender of status' do
expect(sender.statuses.count).to eq 2
end
end
context 'and the relay is disabled' do
before do
subject.perform
end
let(:object_json) do
{
id: 'https://example.com/actor#bar',
type: 'Note',
content: 'Lorem ipsum',
to: 'http://example.com/followers',
}
end
it 'does not create anything' do
expect(sender.statuses.count).to eq 0
end
end
end
context 'when the sender has no relevance to local activity' do
before do
subject.perform
end
let(:object_json) do
{
id: 'https://example.com/actor#bar',
type: 'Note',
content: 'Lorem ipsum',
to: 'http://example.com/followers',
}
end
it 'does not create anything' do
expect(sender.statuses.count).to eq 0
end
end
end
end

View File

@ -13,8 +13,6 @@ RSpec.describe ActivityPub::Activity::Create do
}.with_indifferent_access
end
subject { described_class.new(json, sender) }
before do
sender.update(uri: ActivityPub::TagManager.instance.uri_for(sender))
@ -23,11 +21,402 @@ RSpec.describe ActivityPub::Activity::Create do
end
describe '#perform' do
before do
subject.perform
context 'when fetching' do
subject { described_class.new(json, sender) }
before do
subject.perform
end
context 'standalone' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.text).to eq 'Lorem ipsum'
end
it 'missing to/cc defaults to direct privacy' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'direct'
end
end
context 'public' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
to: 'https://www.w3.org/ns/activitystreams#Public',
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'public'
end
end
context 'unlisted' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
cc: 'https://www.w3.org/ns/activitystreams#Public',
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'unlisted'
end
end
context 'private' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
to: 'http://example.com/followers',
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'private'
end
end
context 'limited' do
let(:recipient) { Fabricate(:account) }
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
to: ActivityPub::TagManager.instance.uri_for(recipient),
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'limited'
end
it 'creates silent mention' do
status = sender.statuses.first
expect(status.mentions.first).to be_silent
end
end
context 'direct' do
let(:recipient) { Fabricate(:account) }
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
to: ActivityPub::TagManager.instance.uri_for(recipient),
tag: {
type: 'Mention',
href: ActivityPub::TagManager.instance.uri_for(recipient),
},
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'direct'
end
end
context 'as a reply' do
let(:original_status) { Fabricate(:status) }
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.thread).to eq original_status
expect(status.reply?).to be true
expect(status.in_reply_to_account).to eq original_status.account
expect(status.conversation).to eq original_status.conversation
end
end
context 'with mentions' do
let(:recipient) { Fabricate(:account) }
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
tag: [
{
type: 'Mention',
href: ActivityPub::TagManager.instance.uri_for(recipient),
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.mentions.map(&:account)).to include(recipient)
end
end
context 'with mentions missing href' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
tag: [
{
type: 'Mention',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
end
end
context 'with media attachments' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
attachment: [
{
type: 'Document',
mediaType: 'image/png',
url: 'http://example.com/attachment.png',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.media_attachments.map(&:remote_url)).to include('http://example.com/attachment.png')
end
end
context 'with media attachments with focal points' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
attachment: [
{
type: 'Document',
mediaType: 'image/png',
url: 'http://example.com/attachment.png',
focalPoint: [0.5, -0.7],
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.media_attachments.map(&:focus)).to include('0.5,-0.7')
end
end
context 'with media attachments missing url' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
attachment: [
{
type: 'Document',
mediaType: 'image/png',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
end
end
context 'with hashtags' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
tag: [
{
type: 'Hashtag',
href: 'http://example.com/blah',
name: '#test',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.tags.map(&:name)).to include('test')
end
end
context 'with hashtags missing name' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
tag: [
{
type: 'Hashtag',
href: 'http://example.com/blah',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
end
end
context 'with emojis' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum :tinking:',
tag: [
{
type: 'Emoji',
icon: {
url: 'http://example.com/emoji.png',
},
name: 'tinking',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.emojis.map(&:shortcode)).to include('tinking')
end
end
context 'with emojis missing name' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum :tinking:',
tag: [
{
type: 'Emoji',
icon: {
url: 'http://example.com/emoji.png',
},
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
end
end
context 'with emojis missing icon' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum :tinking:',
tag: [
{
type: 'Emoji',
name: 'tinking',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
end
end
end
context 'standalone' do
context 'when sender is followed by local users' do
subject { described_class.new(json, sender, delivery: true) }
before do
Fabricate(:account).follow!(sender)
subject.perform
end
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
@ -42,78 +431,23 @@ RSpec.describe ActivityPub::Activity::Create do
expect(status).to_not be_nil
expect(status.text).to eq 'Lorem ipsum'
end
it 'missing to/cc defaults to direct privacy' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'direct'
end
end
context 'public' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
to: 'https://www.w3.org/ns/activitystreams#Public',
}
context 'when sender replies to local status' do
let!(:local_status) { Fabricate(:status) }
subject { described_class.new(json, sender, delivery: true) }
before do
subject.perform
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'public'
end
end
context 'unlisted' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
cc: 'https://www.w3.org/ns/activitystreams#Public',
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'unlisted'
end
end
context 'private' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
to: 'http://example.com/followers',
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'private'
end
end
context 'limited' do
let(:recipient) { Fabricate(:account) }
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
to: ActivityPub::TagManager.instance.uri_for(recipient),
inReplyTo: ActivityPub::TagManager.instance.uri_for(local_status),
}
end
@ -121,28 +455,25 @@ RSpec.describe ActivityPub::Activity::Create do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'limited'
end
it 'creates silent mention' do
status = sender.statuses.first
expect(status.mentions.first).to be_silent
expect(status.text).to eq 'Lorem ipsum'
end
end
context 'direct' do
let(:recipient) { Fabricate(:account) }
context 'when sender targets a local user' do
let!(:local_account) { Fabricate(:account) }
subject { described_class.new(json, sender, delivery: true) }
before do
subject.perform
end
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
to: ActivityPub::TagManager.instance.uri_for(recipient),
tag: {
type: 'Mention',
href: ActivityPub::TagManager.instance.uri_for(recipient),
},
to: ActivityPub::TagManager.instance.uri_for(local_account),
}
end
@ -150,19 +481,25 @@ RSpec.describe ActivityPub::Activity::Create do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.visibility).to eq 'direct'
expect(status.text).to eq 'Lorem ipsum'
end
end
context 'as a reply' do
let(:original_status) { Fabricate(:status) }
context 'when sender cc\'s a local user' do
let!(:local_account) { Fabricate(:account) }
subject { described_class.new(json, sender, delivery: true) }
before do
subject.perform
end
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
inReplyTo: ActivityPub::TagManager.instance.uri_for(original_status),
cc: ActivityPub::TagManager.instance.uri_for(local_account),
}
end
@ -170,240 +507,27 @@ RSpec.describe ActivityPub::Activity::Create do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.thread).to eq original_status
expect(status.reply?).to be true
expect(status.in_reply_to_account).to eq original_status.account
expect(status.conversation).to eq original_status.conversation
expect(status.text).to eq 'Lorem ipsum'
end
end
context 'with mentions' do
let(:recipient) { Fabricate(:account) }
context 'when the sender has no relevance to local activity' do
subject { described_class.new(json, sender, delivery: true) }
before do
subject.perform
end
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
tag: [
{
type: 'Mention',
href: ActivityPub::TagManager.instance.uri_for(recipient),
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.mentions.map(&:account)).to include(recipient)
end
end
context 'with mentions missing href' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
tag: [
{
type: 'Mention',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
end
end
context 'with media attachments' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
attachment: [
{
type: 'Document',
mediaType: 'image/png',
url: 'http://example.com/attachment.png',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.media_attachments.map(&:remote_url)).to include('http://example.com/attachment.png')
end
end
context 'with media attachments with focal points' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
attachment: [
{
type: 'Document',
mediaType: 'image/png',
url: 'http://example.com/attachment.png',
focalPoint: [0.5, -0.7],
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.media_attachments.map(&:focus)).to include('0.5,-0.7')
end
end
context 'with media attachments missing url' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
attachment: [
{
type: 'Document',
mediaType: 'image/png',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
end
end
context 'with hashtags' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
tag: [
{
type: 'Hashtag',
href: 'http://example.com/blah',
name: '#test',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.tags.map(&:name)).to include('test')
end
end
context 'with hashtags missing name' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum',
tag: [
{
type: 'Hashtag',
href: 'http://example.com/blah',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
end
end
context 'with emojis' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum :tinking:',
tag: [
{
type: 'Emoji',
icon: {
url: 'http://example.com/emoji.png',
},
name: 'tinking',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
expect(status.emojis.map(&:shortcode)).to include('tinking')
end
end
context 'with emojis missing name' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum :tinking:',
tag: [
{
type: 'Emoji',
icon: {
url: 'http://example.com/emoji.png',
},
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
end
end
context 'with emojis missing icon' do
let(:object_json) do
{
id: [ActivityPub::TagManager.instance.uri_for(sender), '#bar'].join,
type: 'Note',
content: 'Lorem ipsum :tinking:',
tag: [
{
type: 'Emoji',
name: 'tinking',
},
],
}
end
it 'creates status' do
status = sender.statuses.first
expect(status).to_not be_nil
it 'does not create anything' do
expect(sender.statuses.count).to eq 0
end
end
end

View File

@ -74,10 +74,36 @@ RSpec.describe Formatter do
end
context 'given a URL with a query string' do
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' }
context 'with escaped unicode character' do
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' }
it 'matches the full URL' do
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink"'
it 'matches the full URL' do
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink"'
end
end
context 'with unicode character' do
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓&q=autolink' }
it 'matches the full URL' do
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&q=autolink"'
end
end
context 'with unicode character at the end' do
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓' }
it 'matches the full URL' do
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"'
end
end
context 'with escaped and not escaped unicode characters' do
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink' }
it 'preserves escaped unicode characters' do
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink"'
end
end
end
@ -89,6 +115,22 @@ RSpec.describe Formatter do
end
end
context 'given a URL in quotation marks' do
let(:text) { '"https://example.com/"' }
it 'does not match the quotation marks' do
is_expected.to include 'href="https://example.com/"'
end
end
context 'given a URL in angle brackets' do
let(:text) { '<https://example.com/>' }
it 'does not match the angle brackets' do
is_expected.to include 'href="https://example.com/"'
end
end
context 'given a URL with Japanese path string' do
let(:text) { 'https://ja.wikipedia.org/wiki/日本' }
@ -105,6 +147,22 @@ RSpec.describe Formatter do
end
end
context 'given a URL with a full-width space' do
let(:text) { 'https://example.com/ abc123' }
it 'does not match the full-width space' do
is_expected.to include 'href="https://example.com/"'
end
end
context 'given a URL in Japanese quotation marks' do
let(:text) { '「[https://example.org/」' }
it 'does not match the quotation marks' do
is_expected.to include 'href="https://example.org/"'
end
end
context 'given a URL with Simplified Chinese path string' do
let(:text) { 'https://baike.baidu.com/item/中华人民共和国' }
@ -124,7 +182,11 @@ RSpec.describe Formatter do
context 'given a URL containing unsafe code (XSS attack, visible part)' do
let(:text) { %q{http://example.com/b<del>b</del>} }
it 'escapes the HTML in the URL' do
it 'does not include the HTML in the URL' do
is_expected.to include '"http://example.com/b"'
end
it 'escapes the HTML' do
is_expected.to include '&lt;del&gt;b&lt;/del&gt;'
end
end
@ -132,7 +194,11 @@ RSpec.describe Formatter do
context 'given a URL containing unsafe code (XSS attack, invisible part)' do
let(:text) { %q{http://example.com/blahblahblahblah/a<script>alert("Hello")</script>} }
it 'escapes the HTML in the URL' do
it 'does not include the HTML in the URL' do
is_expected.to include '"http://example.com/blahblahblahblah/a"'
end
it 'escapes the HTML' do
is_expected.to include '&lt;script&gt;alert(&quot;Hello&quot;)&lt;/script&gt;'
end
end
@ -168,6 +234,14 @@ RSpec.describe Formatter do
is_expected.to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>'
end
end
context 'given text containing a hashtag with Unicode chars' do
let(:text) { '#hashtagタグ' }
it 'creates a hashtag link' do
is_expected.to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
end
end
end
describe '#format_spoiler' do

View File

@ -11,6 +11,7 @@ describe EmailMxValidator do
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
@ -23,7 +24,9 @@ describe EmailMxValidator do
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([double(exchange: 'mail.example.com')])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
@ -37,6 +40,21 @@ describe EmailMxValidator do
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([double(address: '1.2.3.4')])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
subject.validate(user)
expect(user.errors).to have_received(:add)
end
it 'adds an error if the AAAA record is blacklisted' do
EmailDomainBlock.create!(domain: 'fd00::1')
resolver = double
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([double(address: 'fd00::1')])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
@ -50,7 +68,25 @@ describe EmailMxValidator do
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([double(exchange: 'mail.example.com')])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([double(address: '2.3.4.5')])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
subject.validate(user)
expect(user.errors).to have_received(:add)
end
it 'adds an error if the MX IPv6 record is blacklisted' do
EmailDomainBlock.create!(domain: 'fd00::2')
resolver = double
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([double(exchange: 'mail.example.com')])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([double(address: 'fd00::2')])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)
@ -64,7 +100,9 @@ describe EmailMxValidator do
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::MX).and_return([double(exchange: 'mail.example.com')])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::A).and_return([])
allow(resolver).to receive(:getresources).with('example.com', Resolv::DNS::Resource::IN::AAAA).and_return([])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::A).and_return([double(address: '2.3.4.5')])
allow(resolver).to receive(:getresources).with('mail.example.com', Resolv::DNS::Resource::IN::AAAA).and_return([double(address: 'fd00::2')])
allow(resolver).to receive(:timeouts=).and_return(nil)
allow(Resolv::DNS).to receive(:open).and_yield(resolver)