diff options
author | Thong Kuah <tkuah@gitlab.com> | 2019-04-11 09:29:07 +0300 |
---|---|---|
committer | James Lopez <james@gitlab.com> | 2019-04-11 09:29:07 +0300 |
commit | d119d3d1b25aac661e6251addf87b280bd37f0c5 (patch) | |
tree | aeaf0d9503326ec7f51968e8d1de48d83ce90503 /spec/validators/addressable_url_validator_spec.rb | |
parent | 79bf4bdaad438dc0f82771b102f3c07225a428da (diff) |
Align UrlValidator to validate_url gem implementation.
Renamed UrlValidator to AddressableUrlValidator to avoid 'url:' naming collision with ActiveModel::Validations::UrlValidator in 'validates' statement.
Make use of the options attribute of the parent class ActiveModel::EachValidator.
Add more options: allow_nil, allow_blank, message.
Renamed 'protocols' option to 'schemes' to match the option naming from UrlValidator.
Diffstat (limited to 'spec/validators/addressable_url_validator_spec.rb')
-rw-r--r-- | spec/validators/addressable_url_validator_spec.rb | 315 |
1 files changed, 315 insertions, 0 deletions
diff --git a/spec/validators/addressable_url_validator_spec.rb b/spec/validators/addressable_url_validator_spec.rb new file mode 100644 index 00000000000..387e84b2d04 --- /dev/null +++ b/spec/validators/addressable_url_validator_spec.rb @@ -0,0 +1,315 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe AddressableUrlValidator do + let!(:badge) { build(:badge, link_url: 'http://www.example.com') } + subject { validator.validate(badge) } + + include_examples 'url validator examples', described_class::DEFAULT_OPTIONS[:schemes] + + describe 'validations' do + include_context 'invalid urls' + + let(:validator) { described_class.new(attributes: [:link_url]) } + + it 'returns error when url is nil' do + expect(validator.validate_each(badge, :link_url, nil)).to be_falsey + expect(badge.errors.first[1]).to eq validator.options.fetch(:message) + end + + it 'returns error when url is empty' do + expect(validator.validate_each(badge, :link_url, '')).to be_falsey + expect(badge.errors.first[1]).to eq validator.options.fetch(:message) + end + + it 'does not allow urls with CR or LF characters' do + aggregate_failures do + urls_with_CRLF.each do |url| + expect(validator.validate_each(badge, :link_url, url)[0]).to eq 'is blocked: URI is invalid' + end + end + end + + it 'provides all arguments to UrlBlock validate' do + expect(Gitlab::UrlBlocker) + .to receive(:validate!) + .with(badge.link_url, described_class::BLOCKER_VALIDATE_OPTIONS) + .and_return(true) + + subject + + expect(badge.errors).to be_empty + end + end + + context 'by default' do + let(:validator) { described_class.new(attributes: [:link_url]) } + + it 'does not block urls pointing to localhost' do + badge.link_url = 'https://127.0.0.1' + + subject + + expect(badge.errors).to be_empty + end + + it 'does not block urls pointing to the local network' do + badge.link_url = 'https://192.168.1.1' + + subject + + expect(badge.errors).to be_empty + end + + it 'does block nil urls' do + badge.link_url = nil + + subject + + expect(badge.errors).to be_present + end + + it 'does block blank urls' do + badge.link_url = '\n\r \n' + + subject + + expect(badge.errors).to be_present + end + + it 'strips urls' do + badge.link_url = "\n\r\n\nhttps://127.0.0.1\r\n\r\n\n\n\n" + + # It's unusual for a validator to modify its arguments. Some extensions, + # such as attr_encrypted, freeze the string to signal that modifications + # will not be persisted, so freeze this string to ensure the scheme is + # compatible with them. + badge.link_url.freeze + + subject + + expect(badge.errors).to be_empty + expect(badge.link_url).to eq('https://127.0.0.1') + end + end + + context 'when message is set' do + let(:message) { 'is blocked: test message' } + let(:validator) { described_class.new(attributes: [:link_url], allow_nil: false, message: message) } + + it 'does block nil url with provided error message' do + expect(validator.validate_each(badge, :link_url, nil)).to be_falsey + expect(badge.errors.first[1]).to eq message + end + end + + context 'when allow_nil is set to true' do + let(:validator) { described_class.new(attributes: [:link_url], allow_nil: true) } + + it 'does not block nil urls' do + badge.link_url = nil + + subject + + expect(badge.errors).to be_empty + end + end + + context 'when allow_blank is set to true' do + let(:validator) { described_class.new(attributes: [:link_url], allow_blank: true) } + + it 'does not block blank urls' do + badge.link_url = "\n\r \n" + + subject + + expect(badge.errors).to be_empty + end + end + + context 'when allow_localhost is set to false' do + let(:validator) { described_class.new(attributes: [:link_url], allow_localhost: false) } + + it 'blocks urls pointing to localhost' do + badge.link_url = 'https://127.0.0.1' + + subject + + expect(badge.errors).to be_present + end + + context 'when allow_setting_local_requests is set to true' do + it 'does not block urls pointing to localhost' do + expect(described_class) + .to receive(:allow_setting_local_requests?) + .and_return(true) + + badge.link_url = 'https://127.0.0.1' + + subject + + expect(badge.errors).to be_empty + end + end + end + + context 'when allow_local_network is set to false' do + let(:validator) { described_class.new(attributes: [:link_url], allow_local_network: false) } + + it 'blocks urls pointing to the local network' do + badge.link_url = 'https://192.168.1.1' + + subject + + expect(badge.errors).to be_present + end + + context 'when allow_setting_local_requests is set to true' do + it 'does not block urls pointing to local network' do + expect(described_class) + .to receive(:allow_setting_local_requests?) + .and_return(true) + + badge.link_url = 'https://192.168.1.1' + + subject + + expect(badge.errors).to be_empty + end + end + end + + context 'when ports is' do + let(:validator) { described_class.new(attributes: [:link_url], ports: ports) } + + context 'empty' do + let(:ports) { [] } + + it 'does not block any port' do + subject + + expect(badge.errors).to be_empty + end + end + + context 'set' do + let(:ports) { [443] } + + it 'blocks urls with a different port' do + subject + + expect(badge.errors).to be_present + end + end + end + + context 'when enforce_user is' do + let(:url) { 'http://$user@example.com'} + let(:validator) { described_class.new(attributes: [:link_url], enforce_user: enforce_user) } + + context 'true' do + let(:enforce_user) { true } + + it 'checks user format' do + badge.link_url = url + + subject + + expect(badge.errors).to be_present + end + end + + context 'false (default)' do + let(:enforce_user) { false } + + it 'does not check user format' do + badge.link_url = url + + subject + + expect(badge.errors).to be_empty + end + end + end + + context 'when ascii_only is' do + let(:url) { 'https://𝕘itⅼαƄ.com/foo/foo.bar'} + let(:validator) { described_class.new(attributes: [:link_url], ascii_only: ascii_only) } + + context 'true' do + let(:ascii_only) { true } + + it 'prevents unicode characters' do + badge.link_url = url + + subject + + expect(badge.errors).to be_present + end + end + + context 'false (default)' do + let(:ascii_only) { false } + + it 'does not prevent unicode characters' do + badge.link_url = url + + subject + + expect(badge.errors).to be_empty + end + end + end + + context 'when enforce_sanitization is' do + let(:validator) { described_class.new(attributes: [:link_url], enforce_sanitization: enforce_sanitization) } + let(:unsafe_url) { "https://replaceme.com/'><script>alert(document.cookie)</script>" } + let(:safe_url) { 'https://replaceme.com/path/to/somewhere' } + + let(:unsafe_internal_url) do + Gitlab.config.gitlab.protocol + '://' + Gitlab.config.gitlab.host + + "/'><script>alert(document.cookie)</script>" + end + + context 'true' do + let(:enforce_sanitization) { true } + + it 'prevents unsafe urls' do + badge.link_url = unsafe_url + + subject + + expect(badge.errors).to be_present + end + + it 'prevents unsafe internal urls' do + badge.link_url = unsafe_internal_url + + subject + + expect(badge.errors).to be_present + end + + it 'allows safe urls' do + badge.link_url = safe_url + + subject + + expect(badge.errors).to be_empty + end + end + + context 'false' do + let(:enforce_sanitization) { false } + + it 'allows unsafe urls' do + badge.link_url = unsafe_url + + subject + + expect(badge.errors).to be_empty + end + end + end +end |