Made user input parsing more robust in changeset query method. Added tests.
authorMatt Amos <zerebubuth@gmail.com>
Mon, 17 Nov 2008 15:30:46 +0000 (15:30 +0000)
committerMatt Amos <zerebubuth@gmail.com>
Mon, 17 Nov 2008 15:30:46 +0000 (15:30 +0000)
app/controllers/changeset_controller.rb
lib/osm.rb
test/functional/changeset_controller_test.rb

index 5a0be35888039347ea877adc9b5488304424cf45..e16a4e9b37a1f6fa1a537d809f7588421936163f 100644 (file)
@@ -257,8 +257,6 @@ class ChangesetController < ApplicationController
     render :nothing => true, :status => :not_found
   rescue OSM::APIError => ex
     render ex.render_opts
-  rescue String => s
-    render :text => s, :content_type => "text/plain", :status => :bad_request
   end
   
   ##
@@ -317,10 +315,10 @@ class ChangesetController < ApplicationController
   # area restriction.
   def conditions_bbox(bbox)
     unless bbox.nil?
-      raise "Bounding box should be min_lon,min_lat,max_lon,max_lat" unless bbox.count(',') == 3
+      raise OSM::APIBadUserInput.new("Bounding box should be min_lon,min_lat,max_lon,max_lat") unless bbox.count(',') == 3
       bbox = sanitise_boundaries(bbox.split(/,/))
-      raise "Minimum longitude should be less than maximum." unless bbox[0] <= bbox[2]
-      raise "Minimum latitude should be less than maximum." unless bbox[1] <= bbox[3]
+      raise OSM::APIBadUserInput.new("Minimum longitude should be less than maximum.") unless bbox[0] <= bbox[2]
+      raise OSM::APIBadUserInput.new("Minimum latitude should be less than maximum.") unless bbox[1] <= bbox[3]
       return ['min_lon < ? and max_lon > ? and min_lat < ? and max_lat > ?',
               bbox[2] * GeoRecord::SCALE, bbox[0] * GeoRecord::SCALE, bbox[3]* GeoRecord::SCALE, bbox[1] * GeoRecord::SCALE]
     else
@@ -332,6 +330,9 @@ class ChangesetController < ApplicationController
   # restrict changesets to those by a particular user
   def conditions_user(user)
     unless user.nil?
+      # user input checking, we don't have any UIDs < 1
+      raise OSM::APIBadUserInput.new("invalid user ID") if user.to_i < 1
+
       u = User.find(user.to_i)
       # should be able to get changesets of public users only, or 
       # our own changesets regardless of public-ness.
@@ -355,7 +356,11 @@ class ChangesetController < ApplicationController
       # if there is a range, i.e: comma separated, then the first is 
       # low, second is high - same as with bounding boxes.
       if time.count(',') == 1
-        from, to = time.split(/,/).collect { |t| DateTime.parse(t) }
+        # check that we actually have 2 elements in the array
+        times = time.split(/,/)
+        raise OSM::APIBadUserInput.new("bad time range") if times.size != 2 
+
+        from, to = times.collect { |t| DateTime.parse(t) }
         return ['created_at > ? and created_at < ?', from, to]
       else
         # if there is no comma, assume its a lower limit on time
@@ -364,8 +369,12 @@ class ChangesetController < ApplicationController
     else
       return nil
     end
+    # stupid DateTime seems to throw both of these for bad parsing, so
+    # we have to catch both and ensure the correct code path is taken.
   rescue ArgumentError => ex
-    raise ex.message.to_s
+    raise OSM::APIBadUserInput.new(ex.message.to_s)
+  rescue RuntimeError => ex
+    raise OSM::APIBadUserInput.new(ex.message.to_s)
   end
 
   ##
index 128e65aac41a73d12d28a2f86a3aa396cf27eb9c..00215c6773e74f2e5895e407fd1ef9a809a3f094 100644 (file)
@@ -141,6 +141,18 @@ module OSM
     end
   end
 
+  ##
+  # raised when user input couldn't be parsed
+  class APIBadUserInput < APIError
+    def initialize(message)
+      @message = message
+    end
+
+    def render_opts
+      { :text => message, :mime_type => "text/plain", :status => :bad_request }
+    end
+  end
+
   # Helper methods for going to/from mercator and lat/lng.
   class Mercator
     include Math
index d5fc08116f6d6fda9076f106cdb20feebc701710..8ccdec889505f3166f9c846e8698abfd1a7249c8 100644 (file)
@@ -592,8 +592,8 @@ EOF
   end
 
   ##
-  # check searching for changesets by bbox
-  def test_changeset_by_bbox
+  # test the query functionality of changesets
+  def test_query
     get :query, :bbox => "-10,-10, 10, 10"
     assert_response :success, "can't get changesets in bbox"
     assert_changesets [1,4]
@@ -629,6 +629,38 @@ EOF
     assert_changesets [4,5]
   end
 
+  ##
+  # check that errors are returned if garbage is inserted 
+  # into query strings
+  def test_query_invalid
+    [ "abracadabra!",
+      "1,2,3,F",
+      ";drop table users;"
+      ].each do |bbox|
+      get :query, :bbox => bbox
+      assert_response :bad_request, "'#{bbox}' isn't a bbox"
+    end
+
+    [ "now()",
+      "00-00-00",
+      ";drop table users;",
+      ",",
+      "-,-"
+      ].each do |time|
+      get :query, :time => time
+      assert_response :bad_request, "'#{time}' isn't a valid time range"
+    end
+
+    [ "me",
+      "foobar",
+      "-1",
+      "0"
+      ].each do |uid|
+      get :query, :user => uid
+      assert_response :bad_request, "'#{uid}' isn't a valid user ID"
+    end
+  end
+
   ##
   # check updating tags on a changeset
   def test_changeset_update