aboutsummaryrefslogtreecommitdiffstats
path: root/apps/settings/lib/Controller/AppSettingsController.php
diff options
context:
space:
mode:
Diffstat (limited to 'apps/settings/lib/Controller/AppSettingsController.php')
-rw-r--r--apps/settings/lib/Controller/AppSettingsController.php68
1 files changed, 59 insertions, 9 deletions
diff --git a/apps/settings/lib/Controller/AppSettingsController.php b/apps/settings/lib/Controller/AppSettingsController.php
index 4d34b7678d7..a85ee8cc20a 100644
--- a/apps/settings/lib/Controller/AppSettingsController.php
+++ b/apps/settings/lib/Controller/AppSettingsController.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -15,13 +16,13 @@ use OC\App\AppStore\Version\VersionParser;
use OC\App\DependencyAnalyzer;
use OC\App\Platform;
use OC\Installer;
+use OCA\AppAPI\Service\ExAppsPageService;
use OCP\App\AppPathNotFoundException;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\NoCSRFRequired;
use OCP\AppFramework\Http\Attribute\OpenAPI;
use OCP\AppFramework\Http\Attribute\PasswordConfirmationRequired;
-use OCP\AppFramework\Http\Attribute\PublicPage;
use OCP\AppFramework\Http\ContentSecurityPolicy;
use OCP\AppFramework\Http\FileDisplayResponse;
use OCP\AppFramework\Http\JSONResponse;
@@ -38,11 +39,14 @@ use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\IGroup;
+use OCP\IGroupManager;
use OCP\IL10N;
use OCP\INavigationManager;
use OCP\IRequest;
use OCP\IURLGenerator;
+use OCP\IUserSession;
use OCP\L10N\IFactory;
+use OCP\Security\RateLimiting\ILimiter;
use OCP\Server;
use OCP\Util;
use Psr\Log\LoggerInterface;
@@ -92,9 +96,9 @@ class AppSettingsController extends Controller {
$this->initialState->provideInitialState('appstoreDeveloperDocs', $this->urlGenerator->linkToDocs('developer-manual'));
$this->initialState->provideInitialState('appstoreUpdateCount', count($this->getAppsWithUpdates()));
- if ($this->appManager->isInstalled('app_api')) {
+ if ($this->appManager->isEnabledForAnyone('app_api')) {
try {
- Server::get(\OCA\AppAPI\Service\ExAppsPageService::class)->provideAppApiState($this->initialState);
+ Server::get(ExAppsPageService::class)->provideAppApiState($this->initialState);
} catch (\Psr\Container\NotFoundExceptionInterface|\Psr\Container\ContainerExceptionInterface $e) {
}
}
@@ -126,10 +130,11 @@ class AppSettingsController extends Controller {
* @param string $image
* @throws \Exception
*/
- #[PublicPage]
#[NoCSRFRequired]
- public function getAppDiscoverMedia(string $fileName): Response {
- $etag = $this->discoverFetcher->getETag() ?? date('Y-m');
+ public function getAppDiscoverMedia(string $fileName, ILimiter $limiter, IUserSession $session): Response {
+ $getEtag = $this->discoverFetcher->getETag() ?? date('Y-m');
+ $etag = trim($getEtag, '"');
+
$folder = null;
try {
$folder = $this->appData->getFolder('app-discover-cache');
@@ -156,6 +161,26 @@ class AppSettingsController extends Controller {
$file = reset($file);
// If not found request from Web
if ($file === false) {
+ $user = $session->getUser();
+ // this route is not public thus we can assume a user is logged-in
+ assert($user !== null);
+ // Register a user request to throttle fetching external data
+ // this will prevent using the server for DoS of other systems.
+ $limiter->registerUserRequest(
+ 'settings-discover-media',
+ // allow up to 24 media requests per hour
+ // this should be a sane default when a completely new section is loaded
+ // keep in mind browsers request all files from a source-set
+ 24,
+ 60 * 60,
+ $user,
+ );
+
+ if (!$this->checkCanDownloadMedia($fileName)) {
+ $this->logger->warning('Tried to load media files for app discover section from untrusted source');
+ return new NotFoundResponse(Http::STATUS_BAD_REQUEST);
+ }
+
try {
$client = $this->clientService->newClient();
$fileResponse = $client->get($fileName);
@@ -177,6 +202,31 @@ class AppSettingsController extends Controller {
return $response;
}
+ private function checkCanDownloadMedia(string $filename): bool {
+ $urlInfo = parse_url($filename);
+ if (!isset($urlInfo['host']) || !isset($urlInfo['path'])) {
+ return false;
+ }
+
+ // Always allowed hosts
+ if ($urlInfo['host'] === 'nextcloud.com') {
+ return true;
+ }
+
+ // Hosts that need further verification
+ // Github is only allowed if from our organization
+ $ALLOWED_HOSTS = ['github.com', 'raw.githubusercontent.com'];
+ if (!in_array($urlInfo['host'], $ALLOWED_HOSTS)) {
+ return false;
+ }
+
+ if (str_starts_with($urlInfo['path'], '/nextcloud/') || str_starts_with($urlInfo['path'], '/nextcloud-gmbh/')) {
+ return true;
+ }
+
+ return false;
+ }
+
/**
* Remove orphaned folders from the image cache that do not match the current etag
* @param ISimpleFolder $folder The folder to clear
@@ -440,7 +490,7 @@ class AppSettingsController extends Controller {
}
$currentVersion = '';
- if ($this->appManager->isInstalled($app['id'])) {
+ if ($this->appManager->isEnabledForAnyone($app['id'])) {
$currentVersion = $this->appManager->getAppVersion($app['id']);
} else {
$currentVersion = $app['releases'][0]['version'];
@@ -519,7 +569,7 @@ class AppSettingsController extends Controller {
// Check if app is already downloaded
/** @var Installer $installer */
- $installer = \OC::$server->get(Installer::class);
+ $installer = Server::get(Installer::class);
$isDownloaded = $installer->isDownloaded($appId);
if (!$isDownloaded) {
@@ -545,7 +595,7 @@ class AppSettingsController extends Controller {
}
private function getGroupList(array $groups) {
- $groupManager = \OC::$server->getGroupManager();
+ $groupManager = Server::get(IGroupManager::class);
$groupsList = [];
foreach ($groups as $group) {
$groupItem = $groupManager->get($group);