CVE-2024-34701: Insufficient Information (NVD-CWE-noinfo)
value: CreateWiki is Miraheze's MediaWiki extension for requesting & creating wikis. It is possible for users to be considered as the requester of a specific wiki request if their local user ID on any wiki in a wiki farm matches the local ID of the requester at the wiki where the wiki request was made. This allows them to go to that request entry's on Special:RequestWikiQueue on the wiki where their local user ID matches and take any actions that the wiki requester is allowed to take from there.

Commit 02e0f298f8d35155c39aa74193cb7b867432c5b8 fixes the issue. Important note about the fix: This vulnerability has been fixed by disabling access to the REST API and special pages outside of the wiki configured as the "global wiki" in `$wgCreateWikiGlobalWiki` in a user's MediaWiki settings.

As a workaround, it is possible to disable the special pages outside of one's own global wiki by doing something similar to `miraheze/mw-config` commit e5664995fbb8644f9a80b450b4326194f20f9ddc that is adapted to one's own setup. As for the REST API, before the fix, there wasn't any REST endpoint that allowed one to make writes. Regardless, it is possible to also disable it outside of the global wiki by using `$wgCreateWikiDisableRESTAPI` and `$wgConf` in the configuration for one's own wiki farm..
CWE description: There is insufficient information about the issue to classify it; details are unkown or unspecified.
Published: 2024-05-14T15:39Z
Repository: https://github.com/miraheze/CreateWiki
Language: PHP
Files changed: includes/CreateWiki/SpecialCreateWiki.php, includes/EntryPointUtils.php, includes/RequestWiki/Handler/RestWikiRequest.php, includes/RequestWiki/Handler/RestWikiRequestsByUser.php, includes/RequestWiki/SpecialRequestWiki.php, includes/RequestWiki/SpecialRequestWikiQueue.php, includes/RestUtils.php, tests/phpunit/SpecialRequestWikiTest.php
Commit date: 2024-05-03 19:10:34 +0200
Commit message: T12011: Disable the special pages and API outside of the global wiki (#519)

https://issue-tracker.miraheze.org/T12011
Diff: diff --git a/includes/CreateWiki/SpecialCreateWiki.php b/includes/CreateWiki/SpecialCreateWiki.php
index 6bdd5f5f..40436200 100644
--- a/includes/CreateWiki/SpecialCreateWiki.php
+++ b/includes/CreateWiki/SpecialCreateWiki.php
@@ -4,10 +4,11 @@ namespace Miraheze\CreateWiki\CreateWiki;
 
 use Config;
 use FormSpecialPage;
 use Html;
 use MediaWiki\MediaWikiServices;
+use Miraheze\CreateWiki\EntryPointUtils;
 use Miraheze\CreateWiki\Hooks\CreateWikiHookRunner;
 use Miraheze\CreateWiki\WikiManager;
 
 class SpecialCreateWiki extends FormSpecialPage {
 
@@ -21,10 +22,20 @@ class SpecialCreateWiki extends FormSpecialPage {
 
 		$this->config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'CreateWiki' );
 		$this->hookRunner = $hookRunner;
 	}
 
+	public function execute( $par ) {
+		if ( !EntryPointUtils::currentWikiIsGlobalWiki() ) {
+			return $this->getOutput()->addHTML(
+				Html::errorBox( $this->msg( 'createwiki-wikinotglobalwiki' )->escaped() )
+			);
+		}
+
+		parent::execute( $par );
+	}
+
 	protected function getFormFields() {
 		$par = $this->par;
 		$request = $this->getRequest();
 
 		$formDescriptor = [
diff --git a/includes/EntryPointUtils.php b/includes/EntryPointUtils.php
new file mode 100644
index 00000000..aee783d1
--- /dev/null
+++ b/includes/EntryPointUtils.php
@@ -0,0 +1,17 @@
+<?php
+namespace Miraheze\CreateWiki;
+
+use MediaWiki\MediaWikiServices;
+use MediaWiki\WikiMap\WikiMap;
+
+class EntryPointUtils {
+
+	public static function currentWikiIsGlobalWiki(): bool {
+		$config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'CreateWiki' );
+		if ( $config->get( 'CreateWikiGlobalWiki' ) === WikiMap::getCurrentWikiId() ) {
+			return true;
+		} else {
+			return false;
+		}
+	}
+}
diff --git a/includes/RequestWiki/Handler/RestWikiRequest.php b/includes/RequestWiki/Handler/RestWikiRequest.php
index 2e1199ad..1f331127 100644
--- a/includes/RequestWiki/Handler/RestWikiRequest.php
+++ b/includes/RequestWiki/Handler/RestWikiRequest.php
@@ -4,10 +4,11 @@ namespace Miraheze\CreateWiki\RequestWiki\Handler;
 
 use Config;
 use MediaWiki\Config\ConfigFactory;
 use MediaWiki\Rest\SimpleHandler;
 use MediaWiki\User\UserFactory;
+use Miraheze\CreateWiki\RestUtils;
 use Wikimedia\Message\MessageValue;
 use Wikimedia\ParamValidator\ParamValidator;
 use Wikimedia\Rdbms\ILBFactory;
 
 /**
@@ -39,13 +40,11 @@ class RestWikiRequest extends SimpleHandler {
 		$this->dbLoadBalancerFactory = $dbLoadBalancerFactory;
 		$this->userFactory = $userFactory;
 	}
 
 	public function run( $requestID ) {
-		if ( $this->config->get( 'CreateWikiDisableRESTAPI' ) ) {
-			return $this->getResponseFactory()->createLocalizedHttpError( 403, new MessageValue( 'createwiki-rest-disabled' ) );
-		}
+		RestUtils::checkEnv();
 		// Should be kept in sync with RequestWikiRequestViewer's $visibilityConds
 		$visibilityConds = [
 			0 => 'public',
 			1 => 'createwiki-deleterequest',
 			2 => 'createwiki-suppressrequest',
diff --git a/includes/RequestWiki/Handler/RestWikiRequestsByUser.php b/includes/RequestWiki/Handler/RestWikiRequestsByUser.php
index 423e146f..3d8bbead 100644
--- a/includes/RequestWiki/Handler/RestWikiRequestsByUser.php
+++ b/includes/RequestWiki/Handler/RestWikiRequestsByUser.php
@@ -4,10 +4,11 @@ namespace Miraheze\CreateWiki\RequestWiki\Handler;
 
 use Config;
 use MediaWiki\Config\ConfigFactory;
 use MediaWiki\Rest\SimpleHandler;
 use MediaWiki\User\UserFactory;
+use Miraheze\CreateWiki\RestUtils;
 use Wikimedia\Message\MessageValue;
 use Wikimedia\ParamValidator\ParamValidator;
 use Wikimedia\Rdbms\ILBFactory;
 
 /**
@@ -39,13 +40,11 @@ class RestWikiRequestsByUser extends SimpleHandler {
 		$this->dbLoadBalancerFactory = $dbLoadBalancerFactory;
 		$this->userFactory = $userFactory;
 	}
 
 	public function run( $userName ) {
-		if ( $this->config->get( 'CreateWikiDisableRESTAPI' ) ) {
-			return $this->getResponseFactory()->createLocalizedHttpError( 403, new MessageValue( 'createwiki-rest-disabled' ) );
-		}
+		RestUtils::checkEnv();
 		// Should be kept in sync with RequestWikiRequestViewer's $visibilityConds
 		$visibilityConds = [
 			0 => 'public',
 			1 => 'createwiki-deleterequest',
 			2 => 'createwiki-suppressrequest',
diff --git a/includes/RequestWiki/SpecialRequestWiki.php b/includes/RequestWiki/SpecialRequestWiki.php
index 8c11efa1..7fbd974e 100644
--- a/includes/RequestWiki/SpecialRequestWiki.php
+++ b/includes/RequestWiki/SpecialRequestWiki.php
@@ -10,10 +10,11 @@ use FormSpecialPage;
 use Html;
 use ManualLogEntry;
 use MediaWiki\MediaWikiServices;
 use Miraheze\CreateWiki\CreateWikiRegexConstraint;
 use Miraheze\CreateWiki\Hooks\CreateWikiHookRunner;
+use Miraheze\CreateWiki\EntryPointUtils;
 use Title;
 
 class SpecialRequestWiki extends FormSpecialPage {
 
 	/** @var Config */
@@ -27,10 +28,16 @@ class SpecialRequestWiki extends FormSpecialPage {
 		$this->config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'CreateWiki' );
 		$this->hookRunner = $hookRunner;
 	}
 
 	public function execute( $par ) {
+		if ( !EntryPointUtils::currentWikiIsGlobalWiki() ) {
+			return $this->getOutput()->addHTML(
+				Html::errorBox( $this->msg( 'createwiki-wikinotglobalwiki' )->escaped() )
+			);
+		}
+
 		$request = $this->getRequest();
 		$out = $this->getOutput();
 
 		$this->requireLogin( 'requestwiki-notloggedin' );
 		$this->setParameter( $par );
diff --git a/includes/RequestWiki/SpecialRequestWikiQueue.php b/includes/RequestWiki/SpecialRequestWikiQueue.php
index 27dac4e7..a102c199 100644
--- a/includes/RequestWiki/SpecialRequestWikiQueue.php
+++ b/includes/RequestWiki/SpecialRequestWikiQueue.php
@@ -1,11 +1,13 @@
 <?php
 
 namespace Miraheze\CreateWiki\RequestWiki;
 
+use Html;
 use HTMLForm;
 use Miraheze\CreateWiki\Hooks\CreateWikiHookRunner;
+use Miraheze\CreateWiki\EntryPointUtils;
 use SpecialPage;
 
 class SpecialRequestWikiQueue extends SpecialPage {
 
 	/** @var CreateWikiHookRunner */
@@ -16,10 +18,16 @@ class SpecialRequestWikiQueue extends SpecialPage {
 
 		$this->hookRunner = $hookRunner;
 	}
 
 	public function execute( $par ) {
+		if ( !EntryPointUtils::currentWikiIsGlobalWiki() ) {
+			return $this->getOutput()->addHTML(
+				Html::errorBox( $this->msg( 'createwiki-wikinotglobalwiki' )->escaped() )
+			);
+		}
+
 		$this->setHeaders();
 
 		if ( $par === null || $par === '' ) {
 			$this->doPagerStuff();
 		} else {
diff --git a/includes/RestUtils.php b/includes/RestUtils.php
new file mode 100644
index 00000000..9197814f
--- /dev/null
+++ b/includes/RestUtils.php
@@ -0,0 +1,24 @@
+<?php
+
+namespace Miraheze\CreateWiki;
+
+use MediaWiki\MediaWikiServices;
+use MediaWiki\Rest\LocalizedHttpException;
+use Miraheze\CreateWiki\EntryPointUtils;
+use Wikimedia\Message\MessageValue;
+
+class RestUtils {
+
+	/*
+	 * Called from the REST handlers, checks that the current wiki is the global wiki and that the REST API is not disabled
+	 */
+	public static function checkEnv() {
+		if ( !EntryPointUtils::currentWikiIsGlobalWiki() ) {
+			throw new LocalizedHttpException( new MessageValue( 'createwiki-wikinotglobalwiki' ), 403 );
+		}
+		$config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'CreateWiki' );
+		if ( $config->get( 'CreateWikiDisableRESTAPI' ) ) {
+			throw new LocalizedHttpException( new MessageValue( 'createwiki-rest-disabled' ), 403 );
+		}
+	}
+}
diff --git a/tests/phpunit/SpecialRequestWikiTest.php b/tests/phpunit/SpecialRequestWikiTest.php
index 40190d5e..c2cdaf2b 100644
--- a/tests/phpunit/SpecialRequestWikiTest.php
+++ b/tests/phpunit/SpecialRequestWikiTest.php
@@ -3,10 +3,11 @@
 namespace Miraheze\CreateWiki\Tests;
 
 use DerivativeContext;
 use ErrorPageError;
 use MediaWikiIntegrationTestCase;
+use MediaWiki\WikiMap\WikiMap;
 use Miraheze\CreateWiki\Hooks\CreateWikiHookRunner;
 use Miraheze\CreateWiki\RequestWiki\SpecialRequestWiki;
 use SpecialPage;
 use UserNotLoggedIn;
 use Wikimedia\TestingAccessWrapper;
@@ -31,10 +32,11 @@ class SpecialRequestWikiTest extends MediaWikiIntegrationTestCase {
 
 	/**
 	 * @covers ::execute
 	 */
 	public function testExecuteNotLoggedIn() {
+		$this->setMwGlobals( 'wgCreateWikiGlobalWiki', WikiMap::getCurrentWikiId() );
 		$hookRunner = $this->createMock( CreateWikiHookRunner::class );
 		$specialRequestWiki = new SpecialRequestWiki( $hookRunner );
 
 		$this->expectException( UserNotLoggedIn::class );
 		$specialRequestWiki->execute( '' );
@@ -68,10 +70,11 @@ class SpecialRequestWikiTest extends MediaWikiIntegrationTestCase {
 
 	/**
 	 * @covers ::execute
 	 */
 	public function testExecuteLoggedInEmailNotConfirmed() {
+		$this->setMwGlobals( 'wgCreateWikiGlobalWiki', WikiMap::getCurrentWikiId() );
 		$this->setGroupPermissions( 'user', 'requestwiki', true );
 
 		$hookRunner = $this->createMock( CreateWikiHookRunner::class );
 
 		$specialRequestWiki = TestingAccessWrapper::newFromObject(

Vulnerable code: 		if ( $this->config->get( 'CreateWikiDisableRESTAPI' ) ) {
			return $this->getResponseFactory()->createLocalizedHttpError( 403, new MessageValue( 'createwiki-rest-disabled' ) );
		}
		if ( $this->config->get( 'CreateWikiDisableRESTAPI' ) ) {
			return $this->getResponseFactory()->createLocalizedHttpError( 403, new MessageValue( 'createwiki-rest-disabled' ) );
		}
Fixed code: use Miraheze\CreateWiki\EntryPointUtils;
	public function execute( $par ) {
		if ( !EntryPointUtils::currentWikiIsGlobalWiki() ) {
			return $this->getOutput()->addHTML(
				Html::errorBox( $this->msg( 'createwiki-wikinotglobalwiki' )->escaped() )
			);
		}

		parent::execute( $par );
	}

<?php
namespace Miraheze\CreateWiki;

use MediaWiki\MediaWikiServices;
use MediaWiki\WikiMap\WikiMap;

class EntryPointUtils {

	public static function currentWikiIsGlobalWiki(): bool {
		$config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'CreateWiki' );
		if ( $config->get( 'CreateWikiGlobalWiki' ) === WikiMap::getCurrentWikiId() ) {
			return true;
		} else {
			return false;
		}
	}
}
use Miraheze\CreateWiki\RestUtils;
		RestUtils::checkEnv();
use Miraheze\CreateWiki\RestUtils;
		RestUtils::checkEnv();
use Miraheze\CreateWiki\EntryPointUtils;
		if ( !EntryPointUtils::currentWikiIsGlobalWiki() ) {
			return $this->getOutput()->addHTML(
				Html::errorBox( $this->msg( 'createwiki-wikinotglobalwiki' )->escaped() )
			);
		}

use Html;
use Miraheze\CreateWiki\EntryPointUtils;
		if ( !EntryPointUtils::currentWikiIsGlobalWiki() ) {
			return $this->getOutput()->addHTML(
				Html::errorBox( $this->msg( 'createwiki-wikinotglobalwiki' )->escaped() )
			);
		}

<?php

namespace Miraheze\CreateWiki;

use MediaWiki\MediaWikiServices;
use MediaWiki\Rest\LocalizedHttpException;
use Miraheze\CreateWiki\EntryPointUtils;
use Wikimedia\Message\MessageValue;

class RestUtils {

	/*
	 * Called from the REST handlers, checks that the current wiki is the global wiki and that the REST API is not disabled
	 */
	public static function checkEnv() {
		if ( !EntryPointUtils::currentWikiIsGlobalWiki() ) {
			throw new LocalizedHttpException( new MessageValue( 'createwiki-wikinotglobalwiki' ), 403 );
		}
		$config = MediaWikiServices::getInstance()->getConfigFactory()->makeConfig( 'CreateWiki' );
		if ( $config->get( 'CreateWikiDisableRESTAPI' ) ) {
			throw new LocalizedHttpException( new MessageValue( 'createwiki-rest-disabled' ), 403 );
		}
	}
}
use MediaWiki\WikiMap\WikiMap;
		$this->setMwGlobals( 'wgCreateWikiGlobalWiki', WikiMap::getCurrentWikiId() );
		$this->setMwGlobals( 'wgCreateWikiGlobalWiki', WikiMap::getCurrentWikiId() );