Commit 72c00594 authored by James Edwards-Jones's avatar James Edwards-Jones

Allow URLs to be validated as ascii_only

Restricts unicode characters and IDNA deviations
which could be used in a phishing attack
parent 8cd5004b
Pipeline #39392774 failed with stages
in 55 minutes and 40 seconds
......@@ -69,6 +69,7 @@ class UrlValidator < ActiveModel::EachValidator
ports: [],
allow_localhost: true,
allow_local_network: true,
ascii_only: false,
enforce_user: false
}
end
......
......@@ -8,7 +8,7 @@ module Gitlab
BlockedUrlError = Class.new(StandardError)
class << self
def validate!(url, allow_localhost: false, allow_local_network: true, enforce_user: false, ports: [], protocols: [])
def validate!(url, ports: [], protocols: [], allow_localhost: false, allow_local_network: true, ascii_only: false, enforce_user: false)
return true if url.nil?
# Param url can be a string, URI or Addressable::URI
......@@ -22,6 +22,7 @@ module Gitlab
validate_port!(port, ports) if ports.any?
validate_user!(uri.user) if enforce_user
validate_hostname!(uri.hostname)
validate_unicode_restriction!(uri) if ascii_only
begin
addrs_info = Addrinfo.getaddrinfo(uri.hostname, port, nil, :STREAM).map do |addr|
......@@ -91,6 +92,12 @@ module Gitlab
raise BlockedUrlError, "Hostname or IP address invalid"
end
def validate_unicode_restriction!(uri)
return if uri.to_s.ascii_only?
raise BlockedUrlError, "URI must be ascii only #{uri.to_s.dump}"
end
def validate_localhost!(addrs_info)
local_ips = ["::", "0.0.0.0"]
local_ips.concat(Socket.ip_address_list.map(&:ip_address))
......
......@@ -249,6 +249,27 @@ describe Gitlab::UrlBlocker do
end
end
end
context 'when ascii_only is true' do
it 'returns true for unicode domain' do
expect(described_class.blocked_url?('https://𝕘itⅼαƄ.com/foo/foo.bar', ascii_only: true)).to be true
end
it 'returns true for unicode tld' do
expect(described_class.blocked_url?('https://gitlab.ᴄοm/foo/foo.bar', ascii_only: true)).to be true
end
it 'returns true for unicode path' do
expect(described_class.blocked_url?('https://gitlab.com/𝒇οο/𝒇οο.Ƅαꮁ', ascii_only: true)).to be true
end
it 'returns true for IDNA deviations' do
expect(described_class.blocked_url?('https://mißile.com/foo/foo.bar', ascii_only: true)).to be true
expect(described_class.blocked_url?('https://miςςile.com/foo/foo.bar', ascii_only: true)).to be true
expect(described_class.blocked_url?('https://git‍lab.com/foo/foo.bar', ascii_only: true)).to be true
expect(described_class.blocked_url?('https://git‌lab.com/foo/foo.bar', ascii_only: true)).to be true
end
end
end
describe '#validate_hostname!' do
......
......@@ -143,4 +143,33 @@ describe UrlValidator do
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.empty?).to be false
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.empty?).to be true
end
end
end
end
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment