Compare commits
7 commits
develop
...
0.7-develo
Author | SHA1 | Date | |
---|---|---|---|
|
4028588621 | ||
|
1cd08e2f8d | ||
|
dcf5cb3cd3 | ||
|
6d69f6ef47 | ||
|
78514f9eb4 | ||
|
7deed07cd1 | ||
|
597196ad3e |
8 changed files with 68 additions and 28 deletions
|
@ -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.
|
||||
|
|
|
@ -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([
|
||||
|
|
|
@ -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')));
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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 '<div class="user-block"> \
|
||||
<img class="img-circle img-bordered-xs" src="https://www.gravatar.com/avatar/' + data.md5 + '?s=120" alt="User Image"> \
|
||||
<img class="img-circle img-bordered-xs" src="https://www.gravatar.com/avatar/' + escapeHtml(data.md5) + '?s=120" alt="User Image"> \
|
||||
<span class="username"> \
|
||||
<a href="#">' + data.name_first + ' ' + data.name_last +'</a> \
|
||||
<a href="#">' + escapeHtml(data.name_first) + ' ' + escapeHtml(data.name_last) +'</a> \
|
||||
</span> \
|
||||
<span class="description"><strong>' + data.email + '</strong> - ' + data.username + '</span> \
|
||||
<span class="description"><strong>' + escapeHtml(data.email) + '</strong> - ' + escapeHtml(data.username) + '</span> \
|
||||
</div>';
|
||||
},
|
||||
templateSelection: function (data) {
|
||||
return '<div> \
|
||||
<span> \
|
||||
<img class="img-rounded img-bordered-xs" src="https://www.gravatar.com/avatar/' + data.md5 + '?s=120" style="height:28px;margin-top:-4px;" alt="User Image"> \
|
||||
<img class="img-rounded img-bordered-xs" src="https://www.gravatar.com/avatar/' + escapeHtml(data.md5) + '?s=120" style="height:28px;margin-top:-4px;" alt="User Image"> \
|
||||
</span> \
|
||||
<span style="padding-left:5px;"> \
|
||||
' + data.name_first + ' ' + data.name_last + ' (<strong>' + data.email + '</strong>) \
|
||||
' + escapeHtml(data.name_first) + ' ' + escapeHtml(data.name_last) + ' (<strong>' + escapeHtml(data.email) + '</strong>) \
|
||||
</span> \
|
||||
</div>';
|
||||
}
|
||||
|
|
|
@ -259,5 +259,5 @@
|
|||
@section('footer-scripts')
|
||||
@parent
|
||||
{!! Theme::js('vendor/lodash/lodash.js') !!}
|
||||
{!! Theme::js('js/admin/new-server.js') !!}
|
||||
{!! Theme::js('js/admin/new-server.js?v=20201003.100600') !!}
|
||||
@endsection
|
||||
|
|
|
@ -83,6 +83,12 @@
|
|||
@section('footer-scripts')
|
||||
@parent
|
||||
<script>
|
||||
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'),
|
||||
|
@ -102,14 +108,14 @@
|
|||
escapeMarkup: function (markup) { return markup; },
|
||||
minimumInputLength: 2,
|
||||
templateResult: function (data) {
|
||||
if (data.loading) return data.text;
|
||||
if (data.loading) return escapeHtml(data.text);
|
||||
|
||||
return '<div class="user-block"> \
|
||||
<img class="img-circle img-bordered-xs" src="https://www.gravatar.com/avatar/' + data.md5 + '?s=120" alt="User Image"> \
|
||||
<img class="img-circle img-bordered-xs" src="https://www.gravatar.com/avatar/' + escapeHtml(data.md5) + '?s=120" alt="User Image"> \
|
||||
<span class="username"> \
|
||||
<a href="#">' + data.name_first + ' ' + data.name_last +'</a> \
|
||||
<a href="#">' + escapeHtml(data.name_first) + ' ' + escapeHtml(data.name_last) +'</a> \
|
||||
</span> \
|
||||
<span class="description"><strong>' + data.email + '</strong> - ' + data.username + '</span> \
|
||||
<span class="description"><strong>' + escapeHtml(data.email) + '</strong> - ' + escapeHtml(data.username) + '</span> \
|
||||
</div>';
|
||||
},
|
||||
templateSelection: function (data) {
|
||||
|
@ -125,10 +131,10 @@
|
|||
|
||||
return '<div> \
|
||||
<span> \
|
||||
<img class="img-rounded img-bordered-xs" src="https://www.gravatar.com/avatar/' + data.md5 + '?s=120" style="height:28px;margin-top:-4px;" alt="User Image"> \
|
||||
<img class="img-rounded img-bordered-xs" src="https://www.gravatar.com/avatar/' + escapeHtml(data.md5) + '?s=120" style="height:28px;margin-top:-4px;" alt="User Image"> \
|
||||
</span> \
|
||||
<span style="padding-left:5px;"> \
|
||||
' + data.name_first + ' ' + data.name_last + ' (<strong>' + data.email + '</strong>) \
|
||||
' + escapeHtml(data.name_first) + ' ' + escapeHtml(data.name_last) + ' (<strong>' + escapeHtml(data.email) + '</strong>) \
|
||||
</span> \
|
||||
</div>';
|
||||
}
|
||||
|
|
|
@ -78,7 +78,7 @@
|
|||
</tr>
|
||||
@if (! empty($server->description))
|
||||
<tr class="server-description">
|
||||
<td colspan="7"><p class="text-muted small no-margin">{{ str_limit($server->description, 400) }}</p></td>
|
||||
<td colspan="8"><p class="text-muted small no-margin">{{ str_limit($server->description, 400) }}</p></td>
|
||||
</tr>
|
||||
@endif
|
||||
@endforeach
|
||||
|
|
Loading…
Reference in a new issue