Skip to content

Commit 5041ad5

Browse files
committed
validate host value
1 parent 37541c3 commit 5041ad5

4 files changed

Lines changed: 63 additions & 4 deletions

File tree

lib/opencage/geocoder.rb

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
require 'opencage/error'
44
require 'opencage/version'
55
require 'open-uri'
6+
require 'net/http'
67
require 'json'
78

89
module OpenCage
@@ -20,12 +21,12 @@ def geocode(location, options = {})
2021
begin
2122
results = fetch(request.to_s)
2223
rescue Errno::ECONNREFUSED
23-
raise_error("408 Failed to open TCP connection to API #{request}")
24+
raise_error("408 Failed to open TCP connection to API #{redact_url(request)}")
2425
rescue Errno::ECONNRESET
2526
# Connection reset by peer - SSL_connect
26-
raise_error("408 Failed to open SSL connection to API #{request}")
27+
raise_error("408 Failed to open SSL connection to API #{redact_url(request)}")
2728
rescue Net::OpenTimeout
28-
raise_error("408 Timeout connecting to API #{request}")
29+
raise_error("408 Timeout connecting to API #{redact_url(request)}")
2930
end
3031

3132
return [] unless results
@@ -53,7 +54,7 @@ def reverse_geocode(lat, lng, options = {})
5354
def fetch(url)
5455
JSON.parse(URI(url).open(headers).read)['results']
5556
rescue OpenURI::HTTPError => e
56-
raise_error(e)
57+
raise_error(e.io.status.join(' '))
5758
end
5859

5960
def headers
@@ -66,6 +67,10 @@ def raise_error(error)
6667
raise klass.new(message: String(error), code: code.to_i)
6768
end
6869

70+
def redact_url(request)
71+
request.to_s.gsub(/key=([^&]{6})[^&]*/, 'key=\1...[REDACTED]')
72+
end
73+
6974
def to_float(coord)
7075
Float(coord)
7176
rescue ArgumentError

lib/opencage/geocoder/request.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,26 @@
11
module OpenCage
22
class Geocoder
33
class Request
4+
LOCALHOST_HOSTS = %w[localhost 127.0.0.1 0.0.0.0 ::1 [::1]].freeze
5+
46
def initialize(api_key, query, options = {})
57
@host = options.fetch(:host, 'api.opencagedata.com')
8+
validate_host!(@host)
69
@params = options.merge(key: api_key, q: query)
710
end
811

12+
private
13+
14+
def validate_host!(host)
15+
hostname = host.to_s.downcase.gsub(/(?<=\w):\d+\z/, '')
16+
return if hostname.end_with?('.opencagedata.com')
17+
return if LOCALHOST_HOSTS.include?(hostname)
18+
19+
raise ArgumentError, "Invalid host: #{host}. Must be a subdomain of opencagedata.com or localhost."
20+
end
21+
22+
public
23+
924
def url
1025
uri = URI::HTTPS.build(host: @host, path: '/geocode/v1/json')
1126
uri.query = URI.encode_www_form(@params)

spec/open_cage/geocoder/request_spec.rb

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,34 @@
1010
it 'forward' do
1111
expect(described_class.new(api_key, 'New York').to_s).to eql('https://api.opencagedata.com/geocode/v1/json?key=1111222233334444&q=New+York')
1212
end
13+
14+
describe 'host validation' do
15+
valid = %w[
16+
api.opencagedata.com
17+
api2.opencagedata.com
18+
localhost
19+
localhost:3000
20+
127.0.0.1
21+
127.0.0.1:8080
22+
0.0.0.0
23+
0.0.0.0:443
24+
::1
25+
]
26+
valid.each do |host|
27+
it "allows #{host}" do
28+
expect { described_class.new(api_key, 'q', host: host) }.not_to raise_error
29+
end
30+
end
31+
32+
invalid = %w[
33+
example.com
34+
opencagedata.com.example.com
35+
notopencagedata.com
36+
]
37+
invalid.each do |host|
38+
it "rejects #{host}" do
39+
expect { described_class.new(api_key, 'q', host: host) }.to raise_error(ArgumentError, /Invalid host/)
40+
end
41+
end
42+
end
1343
end

spec/open_cage/geocoder_spec.rb

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,15 @@ def geo
102102
end
103103
end
104104

105+
describe '#redact_url' do
106+
it 'keeps only the first 6 characters of the API key' do
107+
url = 'https://api.opencagedata.com/geocode/v1/json?key=abcdef1234567890&q=London'
108+
expect(geo.send(:redact_url, url)).to eql(
109+
'https://api.opencagedata.com/geocode/v1/json?key=abcdef...[REDACTED]&q=London'
110+
)
111+
end
112+
end
113+
105114
describe 'user agent' do
106115
before do
107116
stub_request(:get, /api\.opencagedata\.com/)

0 commit comments

Comments
 (0)