diff --git a/CHANGELOG.md b/CHANGELOG.md index 36586d886..8958e82be 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,15 @@ This file is a running track of new features and fixes to each version of the pa This project follows [Semantic Versioning](http://semver.org) guidelines. +## v0.7.19 (Derelict Dermodactylus) +### Fixed +* **[Security]** Fixes XSS in the admin area's server owner selection. + +## v0.7.18 (Derelict Dermodactylus) +### Fixed +* **[Security]** Re-addressed missed endpoint that would not properly limit a user account to 5 API keys. +* **[Security]** Addresses a Client API vulnerability that would allow a user to list all servers on the system ([`GHSA-6888-7f3w-92jx`](https://github.com/pterodactyl/panel/security/advisories/GHSA-6888-7f3w-92jx)) + ## v0.7.17 (Derelict Dermodactylus) ### Fixed * Limited accounts to 5 API keys at a time. diff --git a/app/Http/Controllers/Base/AccountKeyController.php b/app/Http/Controllers/Base/AccountKeyController.php index 7161b4abf..ee1a56832 100644 --- a/app/Http/Controllers/Base/AccountKeyController.php +++ b/app/Http/Controllers/Base/AccountKeyController.php @@ -82,10 +82,13 @@ class AccountKeyController extends Controller */ public function store(StoreAccountKeyRequest $request) { - if ($this->repository->findCountWhere(['user_id' => $request->user()->id]) >= 5) { - throw new DisplayException( - 'Cannot assign more than 5 API keys to an account.' - ); + $count = $this->repository->findCountWhere([ + ['user_id', '=', $request->user()->id], + ['key_type', '=', ApiKey::TYPE_ACCOUNT], + ]); + + if ($count >= 5) { + throw new DisplayException('Cannot assign more than 5 API keys to an account.'); } $this->keyService->setKeyType(ApiKey::TYPE_ACCOUNT)->handle([ diff --git a/app/Http/Controllers/Base/ClientApiController.php b/app/Http/Controllers/Base/ClientApiController.php index a74c28db8..94872ff0c 100644 --- a/app/Http/Controllers/Base/ClientApiController.php +++ b/app/Http/Controllers/Base/ClientApiController.php @@ -8,6 +8,7 @@ use Illuminate\Http\Response; use Pterodactyl\Models\ApiKey; use Illuminate\Http\RedirectResponse; use Prologue\Alerts\AlertsMessageBag; +use Pterodactyl\Exceptions\DisplayException; use Pterodactyl\Http\Controllers\Controller; use Pterodactyl\Services\Api\KeyCreationService; use Pterodactyl\Http\Requests\Base\CreateClientApiKeyRequest; @@ -73,10 +74,20 @@ class ClientApiController extends Controller * @param \Pterodactyl\Http\Requests\Base\CreateClientApiKeyRequest $request * @return \Illuminate\Http\RedirectResponse * + * @throws \Pterodactyl\Exceptions\DisplayException * @throws \Pterodactyl\Exceptions\Model\DataValidationException */ public function store(CreateClientApiKeyRequest $request): RedirectResponse { + $count = $this->repository->findCountWhere([ + ['user_id', '=', $request->user()->id], + ['key_type', '=', ApiKey::TYPE_ACCOUNT], + ]); + + if ($count >= 5) { + throw new DisplayException('Cannot assign more than 5 API keys to an account.'); + } + $allowedIps = null; if (! is_null($request->input('allowed_ips'))) { $allowedIps = json_encode(explode(PHP_EOL, $request->input('allowed_ips'))); diff --git a/app/Repositories/Eloquent/ServerRepository.php b/app/Repositories/Eloquent/ServerRepository.php index 820093cf4..4612d86ed 100644 --- a/app/Repositories/Eloquent/ServerRepository.php +++ b/app/Repositories/Eloquent/ServerRepository.php @@ -225,18 +225,23 @@ class ServerRepository extends EloquentRepository implements ServerRepositoryInt $instance->where('owner_id', $user->id); } - // If set to all, display all servers they can access, including - // those they access as an admin. If set to subuser, only return - // the servers they can access because they are owner, or marked - // as a subuser of the server. - elseif (($level === User::FILTER_LEVEL_ALL && ! $user->root_admin) || $level === User::FILTER_LEVEL_SUBUSER) { - $instance->whereIn('id', $this->getUserAccessServers($user->id)); + // Only allow these two filters if the user is an administrator. + elseif ($user->root_admin && in_array($level, [ User::FILTER_LEVEL_ALL, User::FILTER_LEVEL_ADMIN ])) { + // We specifically only match admin in here. If they request all servers and are a root admin + // we just won't append any filters to the builder and thus they'll be able to see everything + // since this will skip over that final else block. + if ($level === User::FILTER_LEVEL_ADMIN) { + $instance->whereNotIn('id', $this->getUserAccessServers($user->id)); + } } - // If set to admin, only display the servers a user can access - // as an administrator (leaves out owned and subuser of). - elseif ($level === User::FILTER_LEVEL_ADMIN && $user->root_admin) { - $instance->whereNotIn('id', $this->getUserAccessServers($user->id)); + // If we did not match on the user being an administrator and requesting all/admin only or the user + // is not an admin and requested those locked endpoints, just return all of the servers the user actually + // has access to. + // + // @see https://github.com/pterodactyl/panel/security/advisories/GHSA-6888-7f3w-92jx + else { + $instance->whereIn('id', $this->getUserAccessServers($user->id)); } $instance->search($this->getSearchTerm()); diff --git a/public/themes/pterodactyl/js/admin/new-server.js b/public/themes/pterodactyl/js/admin/new-server.js index 97f05487b..b94e804d7 100644 --- a/public/themes/pterodactyl/js/admin/new-server.js +++ b/public/themes/pterodactyl/js/admin/new-server.js @@ -37,6 +37,12 @@ $(document).ready(function() { placeholder: 'Select Additional Allocations', }); + function escapeHtml(str) { + var div = document.createElement('div'); + div.appendChild(document.createTextNode(str)); + return div.innerHTML; + } + $('#pUserId').select2({ ajax: { url: Router.route('admin.users.json'), @@ -56,23 +62,23 @@ $(document).ready(function() { escapeMarkup: function (markup) { return markup; }, minimumInputLength: 2, templateResult: function (data) { - if (data.loading) return data.text; + if (data.loading) return escapeHtml(data.text); return '