Validate URLs against supply-chain attacks

Unfortunately validates_url gem doesn't support mailto or xmpp urls,
so we need to write our own validation.
This commit is contained in:
Andy Allan 2022-11-02 18:21:00 +00:00
parent 7e5cbe87ed
commit af6fec502a
3 changed files with 45 additions and 1 deletions

View file

@ -9,7 +9,13 @@
<p><%= t ".local_chapters.list_text" %></p>
<ul>
<% @local_chapters.each do |chapter| %>
<li><a href="<%= chapter.url %>"><%= t "osm_community_index.communities.#{chapter.id}.name" %></a></li>
<li>
<% if chapter.url %>
<a href="<%= chapter.url %>"><%= t "osm_community_index.communities.#{chapter.id}.name" %></a>
<% else %>
<%= t "osm_community_index.communities.#{chapter.id}.name" %>
<% end %>
</li>
<% end %>
</ul>

View file

@ -8,7 +8,30 @@ module OsmCommunityIndex
def self.load(file_path)
resources = JSON.parse(File.read(file_path))
resources["resources"].values.map! do |v|
v["strings"]["url"] = nil unless valid_url? v["strings"]["url"]
end
resources["resources"].values
end
# This is to avoid any problems if upstream contains urls with `script:` or
# similar schemes, i.e. to guard against supply-chain attacks.
# Unfortunately the validates_url gem doesn't support `mailto:` or similar
# urls. This method is based on their approach to validation.
def self.valid_url?(url)
return true if url.nil?
schemes = %w[http https mailto xmpp]
uri = URI.parse(url)
scheme = uri&.scheme
valid_raw_url = scheme && url =~ /\A#{URI::DEFAULT_PARSER.make_regexp([scheme])}\z/
valid_scheme = scheme && schemes.include?(scheme)
return true if valid_raw_url && valid_scheme
false
rescue URI::InvalidURIError, URI::InvalidComponentError
false
end
end
end

View file

@ -0,0 +1,15 @@
require "test_helper"
class ResourceBackendTest < ActiveSupport::TestCase
def test_valid_url
klass = OsmCommunityIndex::ResourceBackend
assert klass.valid_url?(nil)
assert klass.valid_url?("http://example.com")
assert klass.valid_url?("mailto:bob@example.com?subject=Foo%20Bar")
assert klass.valid_url?("xmpp:osm@jabber.example.org?join")
assert_not klass.valid_url?("javascript:doSomething()")
assert_not klass.valid_url?("foo:[]")
end
end