Skip to content

Commit 97ce501

Browse files
authored
Merge pull request #102 from snh/idn
Improved support for internationalised domains
2 parents 6136713 + cd45272 commit 97ce501

File tree

5 files changed

+145
-7
lines changed

5 files changed

+145
-7
lines changed

lib/github-pages-health-check.rb

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
# frozen_string_literal: true
22

33
require "dnsruby"
4+
require "addressable/idna"
45
require "addressable/uri"
56
require "ipaddr"
67
require "public_suffix"

lib/github-pages-health-check/caa.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@ def records_present?
3333
end
3434

3535
def records
36-
@records ||= (get_caa_records(host) | get_caa_records(PublicSuffix.domain(host)))
36+
unicode_host = Addressable::IDNA.to_unicode(host)
37+
@records ||= begin
38+
get_caa_records(host) | get_caa_records(PublicSuffix.domain(unicode_host))
39+
end
3740
end
3841

3942
private

lib/github-pages-health-check/domain.rb

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ def initialize(host, nameservers: :default)
100100

101101
@host = normalize_host(host)
102102
@nameservers = nameservers
103-
@resolver = GitHubPages::HealthCheck::Resolver.new(host,
103+
@resolver = GitHubPages::HealthCheck::Resolver.new(self.host,
104104
:nameservers => nameservers)
105105
end
106106

@@ -147,7 +147,8 @@ def invalid_cname?
147147
# Used as an escape hatch to prevent false positives on DNS checkes
148148
def valid_domain?
149149
return @valid if defined? @valid
150-
@valid = PublicSuffix.valid?(host, :default_rule => nil)
150+
unicode_host = Addressable::IDNA.to_unicode(host)
151+
@valid = PublicSuffix.valid?(unicode_host, :default_rule => nil)
151152
end
152153

153154
# Is this domain an apex domain, meaning a CNAME would be innapropriate
@@ -160,7 +161,8 @@ def apex_domain?
160161
# It's aware of multi-step top-level domain names:
161162
# E.g. PublicSuffix.domain("blog.digital.gov.uk") # => "digital.gov.uk"
162163
# For apex-level domain names, DNS providers do not support CNAME records.
163-
PublicSuffix.domain(host) == host
164+
unicode_host = Addressable::IDNA.to_unicode(host)
165+
PublicSuffix.domain(unicode_host) == unicode_host
164166
end
165167

166168
# Should the domain use an A record?
@@ -421,8 +423,8 @@ def https_response
421423
# Return the hostname.
422424
def normalize_host(domain)
423425
domain = domain.strip.chomp(".")
424-
host = Addressable::URI.parse(domain).host
425-
host ||= Addressable::URI.parse("http://#{domain}").host
426+
host = Addressable::URI.parse(domain).normalized_host
427+
host ||= Addressable::URI.parse("http://#{domain}").normalized_host
426428
host unless host.to_s.empty?
427429
rescue Addressable::URI::InvalidURIError
428430
nil

lib/github-pages-health-check/resolver.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ def initialize(domain, nameservers: :default)
3232
end
3333

3434
def query(type)
35-
resolver.query(domain, type).answer
35+
resolver.query(Addressable::IDNA.to_ascii(domain), type).answer
3636
end
3737

3838
private

spec/github_pages_health_check/domain_spec.rb

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,30 @@
543543
expect(subject).to be_valid
544544
end
545545
end
546+
547+
context "domains with unicode encoding" do
548+
let(:domain) { "dómain.example.com" }
549+
let(:cname) { "sómething.example.com" }
550+
let(:headers) { { :server => "GitHub.com" } }
551+
before(:each) { allow(subject).to receive(:dns) { [cname_packet] } }
552+
553+
it "doesn't error out on domains with unicode encoding" do
554+
expect(subject).to be_served_by_pages
555+
expect(subject).to be_valid
556+
end
557+
end
558+
559+
context "domains with punycode encoding" do
560+
let(:domain) { "xn--dmain-0ta.example.com" }
561+
let(:cname) { "xn--smething-v3a.example.com" }
562+
let(:headers) { { :server => "GitHub.com" } }
563+
before(:each) { allow(subject).to receive(:dns) { [cname_packet] } }
564+
565+
it "doesn't error out on domains with punycode encoding" do
566+
expect(subject).to be_served_by_pages
567+
expect(subject).to be_valid
568+
end
569+
end
546570
end
547571

548572
context "not served by pages" do
@@ -836,13 +860,27 @@
836860
context "A records pointed to old IPs" do
837861
let(:ip) { "192.30.252.153" }
838862
before(:each) { allow(subject).to receive(:dns) { [a_packet] } }
863+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [a_packet] } }
839864

840865
it { is_expected.not_to be_https_eligible }
866+
867+
context "with unicode encoded domain" do
868+
let(:domain) { "dómain.example.com" }
869+
870+
it { is_expected.not_to be_https_eligible }
871+
end
872+
873+
context "with punycode encoded domain" do
874+
let(:domain) { "xn--dmain-0ta.example.com" }
875+
876+
it { is_expected.not_to be_https_eligible }
877+
end
841878
end
842879

843880
context "A records pointed to new IPs" do
844881
let(:ip) { "185.199.108.153" }
845882
before(:each) { allow(subject).to receive(:dns) { [a_packet] } }
883+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [a_packet] } }
846884

847885
it { is_expected.to be_https_eligible }
848886

@@ -859,11 +897,52 @@
859897

860898
it { is_expected.to be_https_eligible }
861899
end
900+
901+
context "with unicode encoded domain" do
902+
let(:domain) { "dómain.example.com" }
903+
904+
it { is_expected.to be_https_eligible }
905+
906+
context "with bad CAA records" do
907+
let(:caa_domain) { "digicert.com" }
908+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [caa_packet] } }
909+
910+
it { is_expected.not_to be_https_eligible }
911+
end
912+
913+
context "with good CAA records" do
914+
let(:caa_domain) { "letsencrypt.org" }
915+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [caa_packet] } }
916+
917+
it { is_expected.to be_https_eligible }
918+
end
919+
end
920+
921+
context "with punycode encoded domain" do
922+
let(:domain) { "xn--dmain-0ta.example.com" }
923+
924+
it { is_expected.to be_https_eligible }
925+
926+
context "with bad CAA records" do
927+
let(:caa_domain) { "digicert.com" }
928+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [caa_packet] } }
929+
930+
it { is_expected.not_to be_https_eligible }
931+
end
932+
933+
context "with good CAA records" do
934+
let(:caa_domain) { "letsencrypt.org" }
935+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [caa_packet] } }
936+
937+
it { is_expected.to be_https_eligible }
938+
end
939+
end
862940
end
863941

864942
context "CNAME record pointed to username" do
865943
let(:cname) { "foobar.github.io" }
866944
before(:each) { allow(subject).to receive(:dns) { [cname_packet] } }
945+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [cname_packet] } }
867946

868947
it { is_expected.to be_https_eligible }
869948

@@ -880,11 +959,52 @@
880959

881960
it { is_expected.to be_https_eligible }
882961
end
962+
963+
context "with unicode encoded domain" do
964+
let(:domain) { "dómain.example.com" }
965+
966+
it { is_expected.to be_https_eligible }
967+
968+
context "with bad CAA records" do
969+
let(:caa_domain) { "digicert.com" }
970+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [caa_packet] } }
971+
972+
it { is_expected.not_to be_https_eligible }
973+
end
974+
975+
context "with good CAA records" do
976+
let(:caa_domain) { "letsencrypt.org" }
977+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [caa_packet] } }
978+
979+
it { is_expected.to be_https_eligible }
980+
end
981+
end
982+
983+
context "with punycode encoded domain" do
984+
let(:domain) { "xn--dmain-0ta.example.com" }
985+
986+
it { is_expected.to be_https_eligible }
987+
988+
context "with bad CAA records" do
989+
let(:caa_domain) { "digicert.com" }
990+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [caa_packet] } }
991+
992+
it { is_expected.not_to be_https_eligible }
993+
end
994+
995+
context "with good CAA records" do
996+
let(:caa_domain) { "letsencrypt.org" }
997+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [caa_packet] } }
998+
999+
it { is_expected.to be_https_eligible }
1000+
end
1001+
end
8831002
end
8841003

8851004
context "CNAME record pointed elsewhere" do
8861005
let(:cname) { "jinglebells.com" }
8871006
before(:each) { allow(subject).to receive(:dns) { [cname_packet] } }
1007+
before(:each) { allow(subject.send(:caa)).to receive(:query) { [cname_packet] } }
8881008

8891009
it { is_expected.not_to be_https_eligible }
8901010

@@ -901,6 +1021,18 @@
9011021

9021022
it { is_expected.not_to be_https_eligible }
9031023
end
1024+
1025+
context "with unicode encoded domain" do
1026+
let(:domain) { "dómain.example.com" }
1027+
1028+
it { is_expected.not_to be_https_eligible }
1029+
end
1030+
1031+
context "with punycode encoded domain" do
1032+
let(:domain) { "xn--dmain-0ta.example.com" }
1033+
1034+
it { is_expected.not_to be_https_eligible }
1035+
end
9041036
end
9051037
end
9061038
end

0 commit comments

Comments
 (0)