From: Andy Allan Date: Wed, 15 May 2024 16:43:25 +0000 (+0100) Subject: Merge pull request #4800 from AntonKhorev/welcome-stack X-Git-Tag: live~700 X-Git-Url: https://git.openstreetmap.org./rails.git/commitdiff_plain/beeba305e9c55f70e855f1e83fc915b1ccb4206f?hp=fac69da076152ea848bd07df9fd09a40744ff2fd Merge pull request #4800 from AntonKhorev/welcome-stack Stack welcome page columns on small screens --- diff --git a/Gemfile b/Gemfile index 04395b49a..75387b5d5 100644 --- a/Gemfile +++ b/Gemfile @@ -63,6 +63,7 @@ gem "oauth-plugin", ">= 0.5.1" gem "openstreetmap-deadlock_retry", ">= 1.3.1", :require => "deadlock_retry" gem "rack-cors" gem "rails-i18n", "~> 7.0.0" +gem "rails_param" gem "rinku", ">= 2.0.6", :require => "rails_rinku" gem "strong_migrations" gem "validates_email_format_of", ">= 1.5.1" diff --git a/Gemfile.lock b/Gemfile.lock index 087e19f45..14beee830 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -458,6 +458,9 @@ GEM rails-i18n (7.0.9) i18n (>= 0.7, < 2) railties (>= 6.0.0, < 8) + rails_param (1.3.1) + actionpack (>= 3.2.0) + activesupport (>= 3.2.0) railties (7.1.3.2) actionpack (= 7.1.3.2) activesupport (= 7.1.3.2) @@ -672,6 +675,7 @@ DEPENDENCIES rails (~> 7.1.0) rails-controller-testing rails-i18n (~> 7.0.0) + rails_param rinku (>= 2.0.6) rotp rtlcss diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index 5d69a5fc8..f5accc3c4 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -10,6 +10,8 @@ class ApplicationController < ActionController::Base rescue_from CanCan::AccessDenied, :with => :deny_access check_authorization + rescue_from RailsParam::InvalidParameterError, :with => :invalid_parameter + before_action :fetch_body around_action :better_errors_allow_inline, :if => proc { Rails.env.development? } @@ -310,6 +312,17 @@ class ApplicationController < ActionController::Base end end + def invalid_parameter(_exception) + if request.get? + respond_to do |format| + format.html { redirect_to :controller => "/errors", :action => "bad_request" } + format.any { head :bad_request } + end + else + head :bad_request + end + end + # extract authorisation credentials from headers, returns user = nil if none def auth_data if request.env.key? "X-HTTP_AUTHORIZATION" # where mod_rewrite might have put it diff --git a/app/controllers/changesets_controller.rb b/app/controllers/changesets_controller.rb index 6a80f260a..19ec9c91e 100644 --- a/app/controllers/changesets_controller.rb +++ b/app/controllers/changesets_controller.rb @@ -18,6 +18,8 @@ class ChangesetsController < ApplicationController ## # list non-empty changesets in reverse chronological order def index + param! :max_id, Integer, :min => 1 + @params = params.permit(:display_name, :bbox, :friends, :nearby, :max_id, :list) if request.format == :atom && @params[:max_id] diff --git a/app/controllers/concerns/pagination_methods.rb b/app/controllers/concerns/pagination_methods.rb index 3dc9f52aa..79ab10bfb 100644 --- a/app/controllers/concerns/pagination_methods.rb +++ b/app/controllers/concerns/pagination_methods.rb @@ -6,6 +6,9 @@ module PaginationMethods ## # limit selected items to one page, get ids of first item before/after the page def get_page_items(items, includes: [], limit: 20) + param! :before, Integer, :min => 1 + param! :after, Integer, :min => 1 + id_column = "#{items.table_name}.id" page_items = if params[:before] items.where("#{id_column} < ?", params[:before]).order(:id => :desc) diff --git a/app/controllers/errors_controller.rb b/app/controllers/errors_controller.rb index ee1fcca6f..605403348 100644 --- a/app/controllers/errors_controller.rb +++ b/app/controllers/errors_controller.rb @@ -5,6 +5,13 @@ class ErrorsController < ApplicationController before_action :set_locale + def bad_request + respond_to do |format| + format.html { render :status => :bad_request } + format.any { render :status => :bad_request, :plain => "" } + end + end + def forbidden respond_to do |format| format.html { render :status => :forbidden } diff --git a/app/controllers/notes_controller.rb b/app/controllers/notes_controller.rb index 97efc3eda..26d27692e 100644 --- a/app/controllers/notes_controller.rb +++ b/app/controllers/notes_controller.rb @@ -16,6 +16,8 @@ class NotesController < ApplicationController ## # Display a list of notes by a specified user def index + param! :page, Integer, :min => 1 + @params = params.permit(:display_name) @title = t ".title", :user => @user.display_name @page = (params[:page] || 1).to_i diff --git a/app/views/errors/bad_request.html.erb b/app/views/errors/bad_request.html.erb new file mode 100644 index 000000000..036517bdd --- /dev/null +++ b/app/views/errors/bad_request.html.erb @@ -0,0 +1,3 @@ +

<%= t ".title" %>

+

<%= t ".description" %>

+<%= render :partial => "contact" %> diff --git a/config/brakeman.ignore b/config/brakeman.ignore new file mode 100644 index 000000000..b8fed1d9b --- /dev/null +++ b/config/brakeman.ignore @@ -0,0 +1,29 @@ +{ + "ignored_warnings": [ + { + "warning_type": "HTTP Verb Confusion", + "warning_code": 118, + "fingerprint": "9567bbac855c6ec5552572700ec809d7c1d77f59953e6725aeca54fee5091674", + "check_name": "VerbConfusion", + "message": "Potential HTTP verb confusion. `HEAD` is routed like `GET` but `request.get?` will return `false`", + "file": "app/controllers/application_controller.rb", + "line": 312, + "link": "https://brakemanscanner.org/docs/warning_types/http_verb_confusion/", + "code": "if request.get? then\n respond_to do\n format.html do\n redirect_to(:controller => \"/errors\", :action => \"bad_request\")\n end\n format.any do\n head(:bad_request)\n end\n end\nelse\n head(:bad_request)\nend", + "render_path": null, + "location": { + "type": "method", + "class": "ApplicationController", + "method": "invalid_parameter" + }, + "user_input": "request.get?", + "confidence": "Weak", + "cwe_id": [ + 352 + ], + "note": "" + } + ], + "updated": "2024-04-11 10:07:03 +0100", + "brakeman_version": "6.1.2" +} diff --git a/config/locales/en.yml b/config/locales/en.yml index fdd3077ac..647cf66f2 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -633,6 +633,9 @@ en: contact_url_title: Various contact channels explained contact: contact contact_the_community_html: Feel free to %{contact_link} the OpenStreetMap community if you have found a broken link / bug. Make a note of the exact URL of your request. + bad_request: + title: Bad request + description: The operation you requested on the OpenStreetMap server is not valid (HTTP 400) forbidden: title: Forbidden description: The operation you requested on the OpenStreetMap server is only available to administrators (HTTP 403) diff --git a/config/routes.rb b/config/routes.rb index 8271e7e4d..c44064ba3 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -347,6 +347,7 @@ OpenStreetMap::Application.routes.draw do resources :redactions # errors + match "/400", :to => "errors#bad_request", :via => :all match "/403", :to => "errors#forbidden", :via => :all match "/404", :to => "errors#not_found", :via => :all match "/500", :to => "errors#internal_server_error", :via => :all diff --git a/test/controllers/changesets_controller_test.rb b/test/controllers/changesets_controller_test.rb index 3d264181c..a486e4b5e 100644 --- a/test/controllers/changesets_controller_test.rb +++ b/test/controllers/changesets_controller_test.rb @@ -92,6 +92,15 @@ class ChangesetsControllerTest < ActionDispatch::IntegrationTest check_index_result(changesets.last(20)) end + ## + # This should report an error + def test_index_invalid_xhr + %w[-1 0 fred].each do |id| + get history_path(:format => "html", :list => "1", :max_id => id) + assert_redirected_to :controller => :errors, :action => :bad_request + end + end + ## # This should display the last 20 changesets closed in a specific area def test_index_bbox diff --git a/test/controllers/diary_entries_controller_test.rb b/test/controllers/diary_entries_controller_test.rb index 2b10402fa..d13a50163 100644 --- a/test/controllers/diary_entries_controller_test.rb +++ b/test/controllers/diary_entries_controller_test.rb @@ -590,6 +590,17 @@ class DiaryEntriesControllerTest < ActionDispatch::IntegrationTest assert_select "li.page-item.disabled span.page-link", :text => "Newer Entries", :count => 1 end + def test_index_invalid_paged + # Try some invalid paged accesses + %w[-1 0 fred].each do |id| + get diary_entries_path(:before => id) + assert_redirected_to :controller => :errors, :action => :bad_request + + get diary_entries_path(:after => id) + assert_redirected_to :controller => :errors, :action => :bad_request + end + end + def test_rss create(:language, :code => "de") create(:diary_entry, :language_code => "en") @@ -899,6 +910,18 @@ class DiaryEntriesControllerTest < ActionDispatch::IntegrationTest assert_response :not_found end + def test_comments_invalid_paged + user = create(:user) + + %w[-1 0 fred].each do |id| + get diary_comments_path(:display_name => user.display_name, :before => id) + assert_redirected_to :controller => :errors, :action => :bad_request + + get diary_comments_path(:display_name => user.display_name, :after => id) + assert_redirected_to :controller => :errors, :action => :bad_request + end + end + def test_subscribe_page user = create(:user) other_user = create(:user) diff --git a/test/controllers/errors_controller_test.rb b/test/controllers/errors_controller_test.rb index 9f8ccee56..253cbbdc0 100644 --- a/test/controllers/errors_controller_test.rb +++ b/test/controllers/errors_controller_test.rb @@ -2,6 +2,10 @@ require "test_helper" class ErrorsControllerTest < ActionDispatch::IntegrationTest def test_routes + assert_routing( + { :path => "/400", :method => :get }, + { :controller => "errors", :action => "bad_request" } + ) assert_routing( { :path => "/403", :method => :get }, { :controller => "errors", :action => "forbidden" } @@ -16,6 +20,11 @@ class ErrorsControllerTest < ActionDispatch::IntegrationTest ) end + def test_bad_request + get "/400" + assert_response :bad_request + end + def test_forbidden get "/403" assert_response :forbidden diff --git a/test/controllers/notes_controller_test.rb b/test/controllers/notes_controller_test.rb index a54334269..4092ad732 100644 --- a/test/controllers/notes_controller_test.rb +++ b/test/controllers/notes_controller_test.rb @@ -83,6 +83,15 @@ class NotesControllerTest < ActionDispatch::IntegrationTest assert_select "table.note_list tbody tr", :count => 10 end + def test_index_invalid_paged + user = create(:user) + + %w[-1 0 fred].each do |page| + get user_notes_path(user, :page => page) + assert_redirected_to :controller => :errors, :action => :bad_request + end + end + def test_empty_page user = create(:user) get user_notes_path(user) diff --git a/test/controllers/traces_controller_test.rb b/test/controllers/traces_controller_test.rb index 73966641e..972cbb3c3 100644 --- a/test/controllers/traces_controller_test.rb +++ b/test/controllers/traces_controller_test.rb @@ -322,6 +322,17 @@ class TracesControllerTest < ActionDispatch::IntegrationTest assert_select "li.page-item a.page-link", :text => "Older Traces", :count => 2 end + def test_index_invalid_paged + # Try some invalid paged accesses + %w[-1 0 fred].each do |id| + get traces_path(:before => id) + assert_redirected_to :controller => :errors, :action => :bad_request + + get traces_path(:after => id) + assert_redirected_to :controller => :errors, :action => :bad_request + end + end + # Check the RSS feed def test_rss user = create(:user) diff --git a/test/controllers/user_blocks_controller_test.rb b/test/controllers/user_blocks_controller_test.rb index a7ab02c75..97f517133 100644 --- a/test/controllers/user_blocks_controller_test.rb +++ b/test/controllers/user_blocks_controller_test.rb @@ -115,6 +115,18 @@ class UserBlocksControllerTest < ActionDispatch::IntegrationTest check_no_page_link "Older Blocks" end + ## + # test the index action with invalid pages + def test_index_invalid_paged + %w[-1 0 fred].each do |id| + get user_blocks_path(:before => id) + assert_redirected_to :controller => :errors, :action => :bad_request + + get user_blocks_path(:after => id) + assert_redirected_to :controller => :errors, :action => :bad_request + end + end + ## # test the show action def test_show @@ -560,6 +572,20 @@ class UserBlocksControllerTest < ActionDispatch::IntegrationTest check_no_page_link "Older Blocks" end + ## + # test the blocks_on action with invalid pages + def test_blocks_on_invalid_paged + user = create(:user) + + %w[-1 0 fred].each do |id| + get user_blocks_on_path(user, :before => id) + assert_redirected_to :controller => :errors, :action => :bad_request + + get user_blocks_on_path(user, :after => id) + assert_redirected_to :controller => :errors, :action => :bad_request + end + end + ## # test the blocks_by action def test_blocks_by @@ -628,6 +654,20 @@ class UserBlocksControllerTest < ActionDispatch::IntegrationTest check_no_page_link "Older Blocks" end + ## + # test the blocks_by action with invalid pages + def test_blocks_by_invalid_paged + user = create(:moderator_user) + + %w[-1 0 fred].each do |id| + get user_blocks_by_path(user, :before => id) + assert_redirected_to :controller => :errors, :action => :bad_request + + get user_blocks_by_path(user, :after => id) + assert_redirected_to :controller => :errors, :action => :bad_request + end + end + private def check_user_blocks_table(user_blocks) diff --git a/test/controllers/users_controller_test.rb b/test/controllers/users_controller_test.rb index c5566e65d..cff52cff2 100644 --- a/test/controllers/users_controller_test.rb +++ b/test/controllers/users_controller_test.rb @@ -558,6 +558,18 @@ class UsersControllerTest < ActionDispatch::IntegrationTest check_no_page_link "Older Users" end + def test_index_get_invalid_paginated + session_for(create(:administrator_user)) + + %w[-1 0 fred].each do |id| + get users_path(:before => id) + assert_redirected_to :controller => :errors, :action => :bad_request + + get users_path(:after => id) + assert_redirected_to :controller => :errors, :action => :bad_request + end + end + private def check_no_page_link(name)