mirror of
https://github.com/discourse/discourse.git
synced 2024-11-22 08:57:10 -06:00
SECURITY: Impose a upper bound on limit params in various controllers
What is the problem here? In multiple controllers, we are accepting a `limit` params but do not impose any upper bound on the values being accepted. Without an upper bound, we may be allowing arbituary users from generating DB queries which may end up exhausing the resources on the server. What is the fix here? A new `fetch_limit_from_params` helper method is introduced in `ApplicationController` that can be used by controller actions to safely get the limit from the params as a default limit and maximum limit has to be set. When an invalid limit params is encountered, the server will respond with the 400 response code.
This commit is contained in:
parent
0976c8fad6
commit
bfc3132bb2
@ -4,9 +4,11 @@ class Admin::ApiController < Admin::AdminController
|
||||
# Note: in the REST API, ApiKeys are referred to simply as "key"
|
||||
# If we used "api_key", then our user provider would try to use the value for authentication
|
||||
|
||||
INDEX_LIMIT = 50
|
||||
|
||||
def index
|
||||
offset = (params[:offset] || 0).to_i
|
||||
limit = (params[:limit] || 50).to_i.clamp(1, 50)
|
||||
limit = fetch_limit_from_params(default: INDEX_LIMIT, max: INDEX_LIMIT)
|
||||
|
||||
keys =
|
||||
ApiKey
|
||||
|
@ -1,6 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::ReportsController < Admin::StaffController
|
||||
REPORTS_LIMIT = 50
|
||||
|
||||
def index
|
||||
reports_methods =
|
||||
["page_view_total_reqs"] +
|
||||
@ -108,10 +110,7 @@ class Admin::ReportsController < Admin::StaffController
|
||||
facets = nil
|
||||
facets = report_params[:facets].map { |s| s.to_s.to_sym } if Array === report_params[:facets]
|
||||
|
||||
limit = nil
|
||||
if report_params.has_key?(:limit) && report_params[:limit].to_i > 0
|
||||
limit = report_params[:limit].to_i
|
||||
end
|
||||
limit = fetch_limit_from_params(params: report_params, default: nil, max: REPORTS_LIMIT)
|
||||
|
||||
filters = nil
|
||||
filters = report_params[:filters] if report_params.has_key?(:filters)
|
||||
|
@ -1,11 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Admin::StaffActionLogsController < Admin::StaffController
|
||||
INDEX_LIMIT = 200
|
||||
|
||||
def index
|
||||
filters = params.slice(*UserHistory.staff_filters + %i[page limit])
|
||||
|
||||
page = (params[:page] || 0).to_i
|
||||
page_size = (params[:limit] || 200).to_i.clamp(1, 200)
|
||||
page_size = fetch_limit_from_params(default: INDEX_LIMIT, max: INDEX_LIMIT)
|
||||
|
||||
staff_action_logs = UserHistory.staff_action_records(current_user, filters)
|
||||
count = staff_action_logs.count
|
||||
|
@ -1084,4 +1084,22 @@ class ApplicationController < ActionController::Base
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_limit_from_params(params: self.params, default:, max:)
|
||||
raise "default limit cannot be greater than max limit" if default.present? && default > max
|
||||
|
||||
if params.has_key?(:limit)
|
||||
limit =
|
||||
begin
|
||||
Integer(params[:limit])
|
||||
rescue ArgumentError
|
||||
raise Discourse::InvalidParameters.new(:limit)
|
||||
end
|
||||
|
||||
raise Discourse::InvalidParameters.new(:limit) if limit < 0 || limit > max
|
||||
limit
|
||||
else
|
||||
default
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -81,8 +81,7 @@ class DirectoryItemsController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
limit = [params[:limit].to_i, PAGE_SIZE].min if params[:limit].to_i > 0
|
||||
limit ||= PAGE_SIZE
|
||||
limit = fetch_limit_from_params(default: PAGE_SIZE, max: PAGE_SIZE)
|
||||
|
||||
result_count = result.count
|
||||
result = result.limit(limit).offset(limit * page).to_a
|
||||
|
@ -5,11 +5,17 @@ class DraftsController < ApplicationController
|
||||
|
||||
skip_before_action :check_xhr, :preload_json
|
||||
|
||||
INDEX_LIMIT = 50
|
||||
|
||||
def index
|
||||
params.permit(:offset)
|
||||
params.permit(:limit)
|
||||
|
||||
stream = Draft.stream(user: current_user, offset: params[:offset], limit: params[:limit])
|
||||
stream =
|
||||
Draft.stream(
|
||||
user: current_user,
|
||||
offset: params[:offset],
|
||||
limit: fetch_limit_from_params(default: nil, max: INDEX_LIMIT),
|
||||
)
|
||||
|
||||
render json: { drafts: stream ? serialize_data(stream, DraftSerializer) : [] }
|
||||
end
|
||||
|
@ -230,15 +230,16 @@ class GroupsController < ApplicationController
|
||||
render "posts/latest", formats: [:rss]
|
||||
end
|
||||
|
||||
MEMBERS_LIMIT = 1_000
|
||||
|
||||
def members
|
||||
group = find_group(:group_id)
|
||||
|
||||
guardian.ensure_can_see_group_members!(group)
|
||||
|
||||
limit = (params[:limit] || 50).to_i
|
||||
limit = fetch_limit_from_params(default: 50, max: MEMBERS_LIMIT)
|
||||
offset = params[:offset].to_i
|
||||
|
||||
raise Discourse::InvalidParameters.new(:limit) if limit < 0 || limit > 1000
|
||||
raise Discourse::InvalidParameters.new(:offset) if offset < 0
|
||||
|
||||
dir = (params[:asc] && params[:asc].present?) ? "ASC" : "DESC"
|
||||
|
@ -5,6 +5,8 @@ class NotificationsController < ApplicationController
|
||||
before_action :ensure_admin, only: %i[create update destroy]
|
||||
before_action :set_notification, only: %i[update destroy]
|
||||
|
||||
INDEX_LIMIT = 50
|
||||
|
||||
def index
|
||||
user =
|
||||
if params[:username] && !params[:recent]
|
||||
@ -25,8 +27,7 @@ class NotificationsController < ApplicationController
|
||||
end
|
||||
|
||||
if params[:recent].present?
|
||||
limit = (params[:limit] || 15).to_i
|
||||
limit = 50 if limit > 50
|
||||
limit = fetch_limit_from_params(default: 15, max: INDEX_LIMIT)
|
||||
|
||||
include_reviewables = false
|
||||
|
||||
|
@ -1,13 +1,15 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class PostActionUsersController < ApplicationController
|
||||
INDEX_LIMIT = 200
|
||||
|
||||
def index
|
||||
params.require(:post_action_type_id)
|
||||
params.require(:id)
|
||||
post_action_type_id = params[:post_action_type_id].to_i
|
||||
|
||||
page = params[:page].to_i
|
||||
page_size = (params[:limit] || 200).to_i
|
||||
page_size = fetch_limit_from_params(default: INDEX_LIMIT, max: INDEX_LIMIT)
|
||||
|
||||
# Find the post, and then determine if they can see the post (if deleted)
|
||||
post = Post.with_deleted.where(id: params[:id].to_i).first
|
||||
|
@ -662,13 +662,15 @@ class PostsController < ApplicationController
|
||||
render body: nil
|
||||
end
|
||||
|
||||
DELETED_POSTS_MAX_LIMIT = 100
|
||||
|
||||
def deleted_posts
|
||||
params.permit(:offset, :limit)
|
||||
guardian.ensure_can_see_deleted_posts!
|
||||
|
||||
user = fetch_user_from_params
|
||||
offset = [params[:offset].to_i, 0].max
|
||||
limit = [(params[:limit] || 60).to_i, 100].min
|
||||
limit = fetch_limit_from_params(default: 60, max: DELETED_POSTS_MAX_LIMIT)
|
||||
|
||||
posts = user_posts(guardian, user.id, offset: offset, limit: limit).where.not(deleted_at: nil)
|
||||
|
||||
|
@ -79,7 +79,10 @@ class TagGroupsController < ApplicationController
|
||||
matches = matches.where("lower(NAME) in (?)", params[:names].map(&:downcase))
|
||||
end
|
||||
|
||||
matches = matches.order("name").limit(params[:limit] || 5)
|
||||
matches =
|
||||
matches.order("name").limit(
|
||||
fetch_limit_from_params(default: 5, max: SiteSetting.max_tag_search_results),
|
||||
)
|
||||
|
||||
render json: {
|
||||
results:
|
||||
|
@ -292,13 +292,8 @@ class TagsController < ::ApplicationController
|
||||
exclude_has_synonyms: params[:excludeHasSynonyms],
|
||||
}
|
||||
|
||||
if params[:limit]
|
||||
begin
|
||||
filter_params[:limit] = Integer(params[:limit])
|
||||
raise Discourse::InvalidParameters.new(:limit) if !filter_params[:limit].positive?
|
||||
rescue ArgumentError
|
||||
raise Discourse::InvalidParameters.new(:limit)
|
||||
end
|
||||
if limit = fetch_limit_from_params(default: nil, max: SiteSetting.max_tag_search_results)
|
||||
filter_params[:limit] = limit
|
||||
end
|
||||
|
||||
filter_params[:category] = Category.find_by_id(params[:categoryId]) if params[:categoryId]
|
||||
|
@ -1182,6 +1182,8 @@ class UsersController < ApplicationController
|
||||
end
|
||||
end
|
||||
|
||||
SEARCH_USERS_LIMIT = 50
|
||||
|
||||
def search_users
|
||||
term = params[:term].to_s.strip
|
||||
|
||||
@ -1204,10 +1206,11 @@ class UsersController < ApplicationController
|
||||
params[:include_staged_users],
|
||||
)
|
||||
options[:last_seen_users] = !!ActiveModel::Type::Boolean.new.cast(params[:last_seen_users])
|
||||
if params[:limit].present?
|
||||
options[:limit] = params[:limit].to_i
|
||||
raise Discourse::InvalidParameters.new(:limit) if options[:limit] <= 0
|
||||
|
||||
if limit = fetch_limit_from_params(default: nil, max: SEARCH_USERS_LIMIT)
|
||||
options[:limit] = limit
|
||||
end
|
||||
|
||||
options[:topic_id] = topic_id if topic_id
|
||||
options[:category_id] = category_id if category_id
|
||||
|
||||
@ -1733,6 +1736,8 @@ class UsersController < ApplicationController
|
||||
render json: success_json
|
||||
end
|
||||
|
||||
BOOKMARKS_LIMIT = 20
|
||||
|
||||
def bookmarks
|
||||
user = fetch_user_from_params
|
||||
guardian.ensure_can_edit!(user)
|
||||
@ -1740,7 +1745,15 @@ class UsersController < ApplicationController
|
||||
|
||||
respond_to do |format|
|
||||
format.json do
|
||||
bookmark_list = UserBookmarkList.new(user: user, guardian: guardian, params: params)
|
||||
bookmark_list =
|
||||
UserBookmarkList.new(
|
||||
user: user,
|
||||
guardian: guardian,
|
||||
search_term: params[:q],
|
||||
page: params[:page],
|
||||
per_page: fetch_limit_from_params(default: nil, max: BOOKMARKS_LIMIT),
|
||||
)
|
||||
|
||||
bookmark_list.load
|
||||
|
||||
if bookmark_list.bookmarks.empty?
|
||||
@ -1789,10 +1802,9 @@ class UsersController < ApplicationController
|
||||
UserBookmarkList.new(
|
||||
user: current_user,
|
||||
guardian: guardian,
|
||||
params: {
|
||||
per_page: USER_MENU_LIST_LIMIT - reminder_notifications.size,
|
||||
},
|
||||
per_page: USER_MENU_LIST_LIMIT - reminder_notifications.size,
|
||||
)
|
||||
|
||||
bookmark_list.load do |query|
|
||||
if exclude_bookmark_ids.present?
|
||||
query.where("bookmarks.id NOT IN (?)", exclude_bookmark_ids)
|
||||
|
@ -8,26 +8,32 @@ class UserBookmarkList
|
||||
attr_reader :bookmarks, :per_page, :has_more
|
||||
attr_accessor :more_bookmarks_url, :bookmark_serializer_opts
|
||||
|
||||
def initialize(user:, guardian:, params:)
|
||||
def initialize(user:, guardian:, search_term: nil, per_page: nil, page: 0)
|
||||
@user = user
|
||||
@guardian = guardian
|
||||
@params = params
|
||||
|
||||
@params.merge!(per_page: PER_PAGE) if params[:per_page].blank?
|
||||
@params[:per_page] = PER_PAGE if @params[:per_page] > PER_PAGE
|
||||
@per_page = per_page || PER_PAGE
|
||||
@per_page = PER_PAGE if @per_page > PER_PAGE
|
||||
|
||||
@search_term = search_term
|
||||
@page = page.to_i
|
||||
|
||||
@bookmarks = []
|
||||
@bookmark_serializer_opts = {}
|
||||
end
|
||||
|
||||
def load(&blk)
|
||||
query = BookmarkQuery.new(user: @user, guardian: @guardian, params: @params)
|
||||
query =
|
||||
BookmarkQuery.new(
|
||||
user: @user,
|
||||
guardian: @guardian,
|
||||
search_term: @search_term,
|
||||
page: @page,
|
||||
per_page: @per_page,
|
||||
)
|
||||
|
||||
@bookmarks = query.list_all(&blk)
|
||||
@has_more = (@params[:page].to_i + 1) * @params[:per_page] < query.count
|
||||
@has_more = (@page.to_i + 1) * @per_page < query.count
|
||||
@bookmarks
|
||||
end
|
||||
|
||||
def per_page
|
||||
@per_page ||= @params[:per_page]
|
||||
end
|
||||
end
|
||||
|
@ -26,19 +26,18 @@ class BookmarkQuery
|
||||
|
||||
attr_reader :guardian, :count
|
||||
|
||||
def initialize(user:, guardian: nil, params: {})
|
||||
def initialize(user:, guardian: nil, search_term: nil, page: nil, per_page: nil)
|
||||
@user = user
|
||||
@params = params
|
||||
@search_term = search_term
|
||||
@guardian = guardian || Guardian.new(@user)
|
||||
@page = @params[:page].to_i
|
||||
@limit = @params[:limit].present? ? @params[:limit].to_i : @params[:per_page]
|
||||
@page = page ? page.to_i : 0
|
||||
@per_page = per_page ? per_page.to_i : 20
|
||||
@count = 0
|
||||
end
|
||||
|
||||
def list_all(&blk)
|
||||
search_term = @params[:q]
|
||||
ts_query = search_term.present? ? Search.ts_query(term: search_term) : nil
|
||||
search_term_wildcard = search_term.present? ? "%#{search_term}%" : nil
|
||||
ts_query = @search_term.present? ? Search.ts_query(term: @search_term) : nil
|
||||
search_term_wildcard = @search_term.present? ? "%#{@search_term}%" : nil
|
||||
|
||||
queries =
|
||||
Bookmark
|
||||
@ -51,7 +50,7 @@ class BookmarkQuery
|
||||
# on a topic and that topic was moved into a private category
|
||||
next if interim_results.blank?
|
||||
|
||||
if search_term.present?
|
||||
if @search_term.present?
|
||||
interim_results =
|
||||
bookmarkable.perform_search_query(interim_results, search_term_wildcard, ts_query)
|
||||
end
|
||||
@ -78,13 +77,14 @@ class BookmarkQuery
|
||||
|
||||
@count = results.count
|
||||
|
||||
results = results.offset(@page * @params[:per_page]) if @page.positive?
|
||||
results = results.offset(@page * @per_page) if @page.positive?
|
||||
|
||||
if updated_results = blk&.call(results)
|
||||
results = updated_results
|
||||
end
|
||||
|
||||
results = results.limit(@limit).to_a
|
||||
results = results.limit(@per_page).to_a
|
||||
|
||||
BookmarkQuery.preload(results, self)
|
||||
results
|
||||
end
|
||||
|
@ -1,11 +1,13 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
class Chat::Api::ChannelsMembershipsController < Chat::Api::ChannelsController
|
||||
INDEX_LIMIT = 50
|
||||
|
||||
def index
|
||||
params.permit(:username, :offset, :limit)
|
||||
|
||||
offset = params[:offset].to_i
|
||||
limit = (params[:limit] || 50).to_i.clamp(1, 50)
|
||||
limit = fetch_limit_from_params(default: INDEX_LIMIT, max: INDEX_LIMIT)
|
||||
|
||||
memberships =
|
||||
Chat::ChannelMembershipsQuery.call(
|
||||
|
@ -4,10 +4,9 @@ RSpec.describe BookmarkQuery do
|
||||
before { SearchIndexer.enable }
|
||||
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
let(:params) { {} }
|
||||
|
||||
def bookmark_query(user: nil, params: nil)
|
||||
BookmarkQuery.new(user: user || self.user, params: params || self.params)
|
||||
def bookmark_query(user: nil, search_term: nil, per_page: nil)
|
||||
BookmarkQuery.new(user: user || self.user, search_term:, per_page:)
|
||||
end
|
||||
|
||||
describe "#list_all" do
|
||||
@ -67,7 +66,7 @@ RSpec.describe BookmarkQuery do
|
||||
expect(bookmarks.map(&:id)).to eq([])
|
||||
end
|
||||
|
||||
context "when q param is provided" do
|
||||
context "when search_term is provided" do
|
||||
let!(:post) do
|
||||
Fabricate(
|
||||
:post,
|
||||
@ -89,17 +88,17 @@ RSpec.describe BookmarkQuery do
|
||||
end
|
||||
|
||||
it "can search by bookmark name" do
|
||||
bookmarks = bookmark_query(params: { q: "check" }).list_all
|
||||
bookmarks = bookmark_query(search_term: "check").list_all
|
||||
expect(bookmarks.map(&:id)).to eq([bookmark3.id])
|
||||
end
|
||||
|
||||
it "can search by post content" do
|
||||
bookmarks = bookmark_query(params: { q: "content" }).list_all
|
||||
bookmarks = bookmark_query(search_term: "content").list_all
|
||||
expect(bookmarks.map(&:id)).to eq([bookmark4.id])
|
||||
end
|
||||
|
||||
it "can search by topic title" do
|
||||
bookmarks = bookmark_query(params: { q: "bugfix" }).list_all
|
||||
bookmarks = bookmark_query(search_term: "bugfix").list_all
|
||||
expect(bookmarks.map(&:id)).to eq([bookmark4.id])
|
||||
end
|
||||
|
||||
@ -111,7 +110,7 @@ RSpec.describe BookmarkQuery do
|
||||
end
|
||||
|
||||
it "allows searching bookmarkables by fields in other tables" do
|
||||
bookmarks = bookmark_query(params: { q: "bookmarkk" }).list_all
|
||||
bookmarks = bookmark_query(search_term: "bookmarkk").list_all
|
||||
expect(bookmarks.map(&:id)).to eq([bookmark5.id])
|
||||
end
|
||||
end
|
||||
@ -205,10 +204,9 @@ RSpec.describe BookmarkQuery do
|
||||
end
|
||||
end
|
||||
|
||||
context "when the limit param is provided" do
|
||||
let(:params) { { limit: 1 } }
|
||||
context "when the per_page is provided" do
|
||||
it "is respected" do
|
||||
expect(bookmark_query.list_all.count).to eq(1)
|
||||
expect(bookmark_query(per_page: 1).list_all.count).to eq(1)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -1,9 +1,8 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe UserBookmarkList do
|
||||
let(:params) { {} }
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
let(:list) { UserBookmarkList.new(user: user, guardian: Guardian.new(user), params: params) }
|
||||
let(:list) { UserBookmarkList.new(user: user, guardian: Guardian.new(user)) }
|
||||
|
||||
before do
|
||||
register_test_bookmarkable
|
||||
@ -21,9 +20,11 @@ RSpec.describe UserBookmarkList do
|
||||
|
||||
it "returns all types of bookmarks" do
|
||||
list.load
|
||||
|
||||
expect(list.bookmarks.map(&:id)).to match_array(
|
||||
[post_bookmark.id, topic_bookmark.id, user_bookmark.id],
|
||||
)
|
||||
|
||||
expect(list.has_more).to eq(false)
|
||||
end
|
||||
|
||||
@ -32,13 +33,14 @@ RSpec.describe UserBookmarkList do
|
||||
end
|
||||
|
||||
context "when the per_page param is too high" do
|
||||
let(:params) { { per_page: 1000 } }
|
||||
|
||||
it "does not allow more than X bookmarks to be requested per page" do
|
||||
22.times do
|
||||
bookmark = Fabricate(:bookmark, user: user, bookmarkable: Fabricate(:post))
|
||||
Fabricate(:topic_user, topic: bookmark.bookmarkable.topic, user: user)
|
||||
end
|
||||
|
||||
list = UserBookmarkList.new(user: user, guardian: Guardian.new(user), per_page: 1000)
|
||||
|
||||
expect(list.load.count).to eq(20)
|
||||
end
|
||||
end
|
||||
|
@ -78,6 +78,7 @@ end
|
||||
# Requires supporting ruby files with custom matchers and macros, etc,
|
||||
# in spec/support/ and its subdirectories.
|
||||
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }
|
||||
Dir[Rails.root.join("spec/requests/examples/*.rb")].each { |f| require f }
|
||||
|
||||
Dir[Rails.root.join("spec/system/helpers/**/*.rb")].each { |f| require f }
|
||||
Dir[Rails.root.join("spec/system/page_objects/**/base.rb")].each { |f| require f }
|
||||
|
@ -20,6 +20,12 @@ RSpec.describe Admin::ApiController do
|
||||
expect(response.parsed_body["keys"].length).to eq(3)
|
||||
end
|
||||
|
||||
describe "when limit params is invalid" do
|
||||
include_examples "invalid limit params",
|
||||
"/admin/api/keys.json",
|
||||
described_class::INDEX_LIMIT
|
||||
end
|
||||
|
||||
it "can paginate results" do
|
||||
get "/admin/api/keys.json?offset=0&limit=2"
|
||||
|
||||
|
@ -30,6 +30,12 @@ RSpec.describe Admin::ReportsController do
|
||||
end
|
||||
|
||||
context "with invalid params" do
|
||||
context "when limit param is invalid" do
|
||||
include_examples "invalid limit params",
|
||||
"/admin/reports/topics.json",
|
||||
described_class::REPORTS_LIMIT
|
||||
end
|
||||
|
||||
context "with nonexistent report" do
|
||||
it "returns not found reports" do
|
||||
get "/admin/reports/bulk.json",
|
||||
@ -154,6 +160,12 @@ RSpec.describe Admin::ReportsController do
|
||||
expect(response.parsed_body["report"]["total"]).to eq(1)
|
||||
end
|
||||
end
|
||||
|
||||
context "when limit param is invalid" do
|
||||
include_examples "invalid limit params",
|
||||
"/admin/reports/topics.json",
|
||||
described_class::REPORTS_LIMIT
|
||||
end
|
||||
end
|
||||
|
||||
describe "when report is scoped to a category" do
|
||||
|
@ -27,6 +27,12 @@ RSpec.describe Admin::StaffActionLogsController do
|
||||
"action_id" => UserHistory.actions[:delete_topic],
|
||||
)
|
||||
end
|
||||
|
||||
describe "when limit params is invalid" do
|
||||
include_examples "invalid limit params",
|
||||
"/admin/logs/staff_action_logs.json",
|
||||
described_class::INDEX_LIMIT
|
||||
end
|
||||
end
|
||||
|
||||
context "when logged in as an admin" do
|
||||
|
@ -30,22 +30,7 @@ RSpec.describe DirectoryItemsController do
|
||||
expect(json["directory_items"].length).to eq(2)
|
||||
end
|
||||
|
||||
it "does not exceed PAGE_SIZE if limit parameter is more than PAGE_SIZE" do
|
||||
large_limit = DirectoryItemsController::PAGE_SIZE + 10
|
||||
get "/directory_items.json", params: { period: "all", limit: large_limit }
|
||||
expect(response.status).to eq(200)
|
||||
json = response.parsed_body
|
||||
|
||||
expect(json["directory_items"].length).to eq(DirectoryItemsController::PAGE_SIZE)
|
||||
end
|
||||
|
||||
it "handles invalid limit parameters gracefully" do
|
||||
get "/directory_items.json", params: { period: "all", limit: "invalid_limit" }
|
||||
expect(response.status).to eq(200)
|
||||
json = response.parsed_body
|
||||
|
||||
expect(json["directory_items"]).not_to be_empty
|
||||
end
|
||||
include_examples "invalid limit params", "/directory_items.json", described_class::PAGE_SIZE
|
||||
end
|
||||
|
||||
context "with exclude_groups parameter" do
|
||||
|
@ -1,14 +1,22 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.describe DraftsController do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
|
||||
describe "#index" do
|
||||
it "requires you to be logged in" do
|
||||
get "/drafts.json"
|
||||
expect(response.status).to eq(403)
|
||||
end
|
||||
|
||||
describe "when limit params is invalid" do
|
||||
before { sign_in(user) }
|
||||
|
||||
include_examples "invalid limit params", "/drafts.json", described_class::INDEX_LIMIT
|
||||
end
|
||||
|
||||
it "returns correct stream length after adding a draft" do
|
||||
user = sign_in(Fabricate(:user))
|
||||
sign_in(user)
|
||||
Draft.set(user, "xxx", 0, "{}")
|
||||
get "/drafts.json"
|
||||
expect(response.status).to eq(200)
|
||||
@ -17,7 +25,7 @@ RSpec.describe DraftsController do
|
||||
end
|
||||
|
||||
it "has empty stream after deleting last draft" do
|
||||
user = sign_in(Fabricate(:user))
|
||||
sign_in(user)
|
||||
Draft.set(user, "xxx", 0, "{}")
|
||||
Draft.clear(user, "xxx", 0)
|
||||
get "/drafts.json"
|
||||
@ -46,7 +54,7 @@ RSpec.describe DraftsController do
|
||||
|
||||
describe "#show" do
|
||||
it "returns a draft if requested" do
|
||||
user = sign_in(Fabricate(:user))
|
||||
sign_in(user)
|
||||
Draft.set(user, "hello", 0, "test")
|
||||
|
||||
get "/drafts/hello.json"
|
||||
@ -62,7 +70,7 @@ RSpec.describe DraftsController do
|
||||
end
|
||||
|
||||
it "saves a draft" do
|
||||
user = sign_in(Fabricate(:user))
|
||||
sign_in(user)
|
||||
|
||||
post "/drafts.json", params: { draft_key: "xyz", data: { my: "data" }.to_json, sequence: 0 }
|
||||
|
||||
@ -77,7 +85,7 @@ RSpec.describe DraftsController do
|
||||
end
|
||||
|
||||
it "checks for an conflict on update" do
|
||||
user = sign_in(Fabricate(:user))
|
||||
sign_in(user)
|
||||
post = Fabricate(:post, user: user)
|
||||
|
||||
post "/drafts.json",
|
||||
@ -103,7 +111,7 @@ RSpec.describe DraftsController do
|
||||
end
|
||||
|
||||
it "cant trivially resolve conflicts without interaction" do
|
||||
user = sign_in(Fabricate(:user))
|
||||
sign_in(user)
|
||||
|
||||
DraftSequence.next!(user, "abc")
|
||||
|
||||
@ -120,7 +128,7 @@ RSpec.describe DraftsController do
|
||||
end
|
||||
|
||||
it "has a clean protocol for ownership handover" do
|
||||
user = sign_in(Fabricate(:user))
|
||||
sign_in(user)
|
||||
|
||||
post "/drafts.json",
|
||||
params: {
|
||||
@ -170,7 +178,7 @@ RSpec.describe DraftsController do
|
||||
end
|
||||
|
||||
it "raises an error for out-of-sequence draft setting" do
|
||||
user = sign_in(Fabricate(:user))
|
||||
sign_in(user)
|
||||
seq = DraftSequence.next!(user, "abc")
|
||||
Draft.set(user, "abc", seq, { b: "test" }.to_json)
|
||||
|
||||
@ -230,7 +238,7 @@ RSpec.describe DraftsController do
|
||||
|
||||
describe "#destroy" do
|
||||
it "destroys drafts when required" do
|
||||
user = sign_in(Fabricate(:user))
|
||||
sign_in(user)
|
||||
Draft.set(user, "xxx", 0, "hi")
|
||||
delete "/drafts/xxx.json", params: { sequence: 0 }
|
||||
|
||||
|
23
spec/requests/examples/invalid_limit_params.rb
Normal file
23
spec/requests/examples/invalid_limit_params.rb
Normal file
@ -0,0 +1,23 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
RSpec.shared_examples "invalid limit params" do |endpoint, max_limit, extra_params|
|
||||
let(:params) { extra_params&.dig(:params) || {} }
|
||||
|
||||
it "returns 400 response code when limit params is negative" do
|
||||
get endpoint, params: { limit: -1 }.merge(params)
|
||||
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it "returns 400 response code when limit params is suspicious" do
|
||||
get endpoint, params: { limit: "1; DROP TABLE users" }.merge(params)
|
||||
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
|
||||
it "returns 400 response code when limit params exceeds the max limit" do
|
||||
get endpoint, params: { limit: max_limit + 1 }.merge(params)
|
||||
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
end
|
@ -87,6 +87,15 @@ RSpec.describe NotificationsController do
|
||||
Discourse.clear_redis_readonly!
|
||||
end
|
||||
|
||||
describe "when limit params is invalid" do
|
||||
include_examples "invalid limit params",
|
||||
"/notifications.json",
|
||||
described_class::INDEX_LIMIT,
|
||||
params: {
|
||||
recent: true,
|
||||
}
|
||||
end
|
||||
|
||||
it "get notifications with all filters" do
|
||||
notification = Fabricate(:notification, user: user)
|
||||
notification2 = Fabricate(:notification, user: user)
|
||||
|
@ -4,6 +4,14 @@ RSpec.describe PostActionUsersController do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
let(:post) { Fabricate(:post, user: sign_in(user)) }
|
||||
|
||||
describe "index" do
|
||||
describe "when limit params is invalid" do
|
||||
include_examples "invalid limit params",
|
||||
"/post_action_users.json",
|
||||
described_class::INDEX_LIMIT
|
||||
end
|
||||
end
|
||||
|
||||
context "with render" do
|
||||
it "always allows you to see your own actions" do
|
||||
notify_mod = PostActionType.types[:notify_moderators]
|
||||
|
@ -2254,6 +2254,14 @@ RSpec.describe PostsController do
|
||||
expect(response).to be_forbidden
|
||||
end
|
||||
|
||||
describe "when limit params is invalid" do
|
||||
before { sign_in(moderator) }
|
||||
|
||||
include_examples "invalid limit params",
|
||||
"/posts/system/deleted.json",
|
||||
described_class::DELETED_POSTS_MAX_LIMIT
|
||||
end
|
||||
|
||||
it "can see the deleted posts when authorized" do
|
||||
sign_in(moderator)
|
||||
get "/posts/system/deleted.json"
|
||||
|
@ -48,6 +48,12 @@ RSpec.describe TagGroupsController do
|
||||
let(:full) { TagGroupPermission.permission_types[:full] }
|
||||
let(:readonly) { TagGroupPermission.permission_types[:readonly] }
|
||||
|
||||
describe "when limit params is invalid" do
|
||||
include_examples "invalid limit params",
|
||||
"/tag_groups/filter/search.json",
|
||||
SiteSetting.max_tag_search_results
|
||||
end
|
||||
|
||||
context "for anons" do
|
||||
it "returns the tag group with the associated tag names" do
|
||||
tag_group = tag_group_with_permission(everyone, readonly)
|
||||
|
@ -1103,22 +1103,10 @@ RSpec.describe TagsController do
|
||||
)
|
||||
end
|
||||
|
||||
it "returns error 400 for negative limit" do
|
||||
get "/tags/filter/search.json", params: { q: "", limit: -1 }
|
||||
|
||||
expect(response.status).to eq(400)
|
||||
expect(response.parsed_body["errors"].first).to eq(
|
||||
I18n.t("invalid_params", message: "limit"),
|
||||
)
|
||||
end
|
||||
|
||||
it "returns error 400 for suspicious limit" do
|
||||
get "/tags/filter/search.json", params: { q: "", limit: "1; SELECT 1" }
|
||||
|
||||
expect(response.status).to eq(400)
|
||||
expect(response.parsed_body["errors"].first).to eq(
|
||||
I18n.t("invalid_params", message: "limit"),
|
||||
)
|
||||
describe "when limit params is invalid" do
|
||||
include_examples "invalid limit params",
|
||||
"/tags/filter/search.json",
|
||||
SiteSetting.max_tag_search_results
|
||||
end
|
||||
|
||||
it "includes required tag group information" do
|
||||
|
@ -4,7 +4,7 @@ require "rotp"
|
||||
|
||||
RSpec.describe UsersController do
|
||||
fab!(:user) { Fabricate(:user) }
|
||||
fab!(:user1) { Fabricate(:user) }
|
||||
fab!(:user1) { Fabricate(:user, username: "someusername") }
|
||||
fab!(:another_user) { Fabricate(:user) }
|
||||
fab!(:invitee) { Fabricate(:user) }
|
||||
fab!(:inviter) { Fabricate(:user) }
|
||||
@ -4886,11 +4886,10 @@ RSpec.describe UsersController do
|
||||
expect(response.status).to eq(200)
|
||||
end
|
||||
|
||||
context "with limit" do
|
||||
it "returns an error if value is invalid" do
|
||||
get "/u/search/users.json", params: { limit: "-1" }
|
||||
expect(response.status).to eq(400)
|
||||
end
|
||||
describe "when limit params is invalid" do
|
||||
include_examples "invalid limit params",
|
||||
"/u/search/users.json",
|
||||
described_class::SEARCH_USERS_LIMIT
|
||||
end
|
||||
|
||||
context "when `enable_names` is true" do
|
||||
@ -6185,6 +6184,14 @@ RSpec.describe UsersController do
|
||||
expect(response.status).to eq(200)
|
||||
expect(response.parsed_body["bookmarks"]).to eq([])
|
||||
end
|
||||
|
||||
describe "when limit params is invalid" do
|
||||
before { sign_in(user1) }
|
||||
|
||||
include_examples "invalid limit params",
|
||||
"/u/someusername/bookmarks.json",
|
||||
described_class::BOOKMARKS_LIMIT
|
||||
end
|
||||
end
|
||||
|
||||
describe "#bookmarks excerpts" do
|
||||
|
@ -18,7 +18,7 @@ RSpec.describe UserBookmarkListSerializer do
|
||||
let(:user_bookmark) { Fabricate(:bookmark, user: user, bookmarkable: Fabricate(:user)) }
|
||||
|
||||
def run_serializer
|
||||
bookmark_list = UserBookmarkList.new(user: user, guardian: Guardian.new(user), params: {})
|
||||
bookmark_list = UserBookmarkList.new(user: user, guardian: Guardian.new(user))
|
||||
bookmark_list.load
|
||||
UserBookmarkListSerializer.new(bookmark_list)
|
||||
end
|
||||
|
Loading…
Reference in New Issue
Block a user