From c103b95651d7e1874cba1fe9fdc3f1a59ca5a3cc Mon Sep 17 00:00:00 2001
From: csteipp <csteipp@wikimedia.org>
Date: Wed, 11 Mar 2015 18:44:44 -0700
Subject: [PATCH] SECURITY: Throttle uploads

Add throttle check in ApiUpload and SpecialUpload.

Bug: T91850
Change-Id: If33cc99f304aab2486507c7500b4abb06b6b5d70
---
 includes/DefaultSettings.php        |  6 ++++++
 includes/api/ApiUpload.php          |  6 ++++++
 includes/specials/SpecialUpload.php |  5 +++++
 includes/upload/UploadBase.php      | 10 ++++++++++
 4 files changed, 27 insertions(+)

diff --git a/includes/DefaultSettings.php b/includes/DefaultSettings.php
index 6f2f5b9..3232262 100644
--- a/includes/DefaultSettings.php
+++ b/includes/DefaultSettings.php
@@ -5174,6 +5174,12 @@ $wgRateLimits = array(
 		'ip' => null, // for each anon and recent account
 		'subnet' => null, // ... within a /24 subnet in IPv4 or /64 in IPv6
 	),
+	'upload' => array(
+		'user' => null,
+		'newbie' => null,
+		'ip' => null,
+		'subnet' => null,
+	),
 	'move' => array(
 		'user' => null,
 		'newbie' => null,
diff --git a/includes/api/ApiUpload.php b/includes/api/ApiUpload.php
index 54294c9..bd75a78 100644
--- a/includes/api/ApiUpload.php
+++ b/includes/api/ApiUpload.php
@@ -138,6 +138,12 @@ class ApiUpload extends ApiBase {
 			return $this->getStashResult( $warnings );
 		}
 
+		// Check throttle after we've handled warnings
+		if ( UploadBase::isThrottled( $this->getUser() )
+		) {
+			$this->dieUsageMsg( 'actionthrottledtext' );
+		}
+
 		// This is the most common case -- a normal upload with no warnings
 		// performUpload will return a formatted properly for the API with status
 		return $this->performUpload( $warnings );
diff --git a/includes/specials/SpecialUpload.php b/includes/specials/SpecialUpload.php
index 6b0bf41..40e2a10 100644
--- a/includes/specials/SpecialUpload.php
+++ b/includes/specials/SpecialUpload.php
@@ -460,6 +460,11 @@ class SpecialUpload extends SpecialPage {
 			}
 		}
 
+		// This is as late as we can throttle, after expected issues have been handled
+		if ( UploadBase::isThrottled( $this->getUser() ) ) {
+			throw new ThrottledError();
+		}
+
 		// Get the page text if this is not a reupload
 		if ( !$this->mForReUpload ) {
 			$pageText = self::getInitialPageText( $this->mComment, $this->mLicense,
diff --git a/includes/upload/UploadBase.php b/includes/upload/UploadBase.php
index 426c752..287c5fe 100644
--- a/includes/upload/UploadBase.php
+++ b/includes/upload/UploadBase.php
@@ -128,6 +128,16 @@ abstract class UploadBase {
 		return true;
 	}
 
+	/**
+	 * Returns true if the user has surpassed the upload rate limit, false otherwise.
+	 *
+	 * @param User $user
+	 * @return bool
+	 */
+	public static function isThrottled( $user ) {
+		return $user->pingLimiter( 'upload' );
+	}
+
 	// Upload handlers. Should probably just be a global.
 	private static $uploadHandlers = array( 'Stash', 'File', 'Url' );
 
-- 
1.8.4.5

