From 003b80e62f97cd8c0114d6b9d3f93c10443e6fae Mon Sep 17 00:00:00 2001 From: Alan Guo Xiang Tan Date: Wed, 7 Feb 2024 12:55:36 +0800 Subject: [PATCH] SECURITY: Add rate limits for uploads --- app/controllers/uploads_controller.rb | 7 +++++++ config/site_settings.yml | 3 +++ spec/requests/uploads_controller_spec.rb | 26 ++++++++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/app/controllers/uploads_controller.rb b/app/controllers/uploads_controller.rb index c4c651e9429..9e9a2c3a340 100644 --- a/app/controllers/uploads_controller.rb +++ b/app/controllers/uploads_controller.rb @@ -25,6 +25,13 @@ class UploadsController < ApplicationController # capture current user for block later on me = current_user + RateLimiter.new( + current_user, + "uploads-per-minute", + SiteSetting.max_uploads_per_minute, + 1.minute.to_i, + ).performed! + params.permit(:type, :upload_type) raise Discourse::InvalidParameters if params[:type].blank? && params[:upload_type].blank? # 50 characters ought to be enough for the upload type diff --git a/config/site_settings.yml b/config/site_settings.yml index 0b91bd188c4..5555c155a8e 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -2228,6 +2228,9 @@ rate_limits: max_complete_multipart_per_minute: default: 10 hidden: true + max_uploads_per_minute: + default: 10 + hidden: true developer: force_hostname: diff --git a/spec/requests/uploads_controller_spec.rb b/spec/requests/uploads_controller_spec.rb index e0acb6fde09..1bca77e3db7 100644 --- a/spec/requests/uploads_controller_spec.rb +++ b/spec/requests/uploads_controller_spec.rb @@ -19,6 +19,32 @@ RSpec.describe UploadsController do let(:fake_jpg) { Rack::Test::UploadedFile.new(file_from_fixtures("fake.jpg")) } let(:text_file) { Rack::Test::UploadedFile.new(File.new("#{Rails.root}/LICENSE.txt")) } + context "when rate limited" do + before { RateLimiter.enable } + + use_redis_snapshotting + + it "should return 429 response code when maximum number of uploads per minute has been exceeded for a user" do + SiteSetting.max_uploads_per_minute = 1 + + post "/uploads.json", + params: { + file: Rack::Test::UploadedFile.new(logo_file), + type: "avatar", + } + + expect(response.status).to eq(200) + + post "/uploads.json", + params: { + file: Rack::Test::UploadedFile.new(logo_file), + type: "avatar", + } + + expect(response.status).to eq(429) + end + end + it "expects a type or upload_type" do post "/uploads.json", params: { file: logo } expect(response.status).to eq(400)