]> git.openstreetmap.org Git - rails.git/commitdiff
Validate URLs against supply-chain attacks
authorAndy Allan <git@gravitystorm.co.uk>
Wed, 2 Nov 2022 18:21:00 +0000 (18:21 +0000)
committerAndy Allan <git@gravitystorm.co.uk>
Wed, 2 Nov 2022 18:21:00 +0000 (18:21 +0000)
Unfortunately validates_url gem doesn't support mailto or xmpp urls,
so we need to write our own validation.

app/views/site/communities.html.erb
lib/osm_community_index/resource_backend.rb
test/lib/osm_community_index/resource_backend_test.rb [new file with mode: 0644]

index ba687966f5cc50867cd631b2c31920b049604117..c2140668379ba951d202a6051c120e1476fcab9e 100644 (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>
 
index e16d951a409805bf1283398239f565bdc5e20173..3ed14e6de3982aedfa3ec6e8336f9f509fe44d1e 100644 (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
diff --git a/test/lib/osm_community_index/resource_backend_test.rb b/test/lib/osm_community_index/resource_backend_test.rb
new file mode 100644 (file)
index 0000000..91b0bb5
--- /dev/null
@@ -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