From 62856556b901def3dcc651e6ee9f114191443c9b Mon Sep 17 00:00:00 2001
From: Stepan Fedotov <TrixterTheTux@users.noreply.github.com>
Date: Sat, 3 Oct 2020 19:55:35 +0300
Subject: [PATCH] Apply security fixes from #2441 to 1.0

---
 CHANGELOG.md                                  | 13 ++++++-
 .../themes/pterodactyl/js/admin/new-server.js | 37 +++++++++++--------
 .../admin/servers/view/details.blade.php      | 18 ++++++---
 3 files changed, 44 insertions(+), 24 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index efd2cef5..8958e82b 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.
@@ -301,7 +310,7 @@ the response from the server `GET` endpoint.
 * Nest and Egg listings now show the associated ID in order to make API requests easier.
 * Added star indicators to user listing in Admin CP to indicate users who are set as a root admin.
 * Creating a new node will now requires a SSL connection if the Panel is configured to use SSL as well.
-* Connector error messages due to permissions are now rendered correctly in the UI rather than causing a silent failure.
+* Socketio error messages due to permissions are now rendered correctly in the UI rather than causing a silent failure.
 * File manager now supports mass deletion option for files and folders.
 * Support for CS:GO as a default service option selection.
 * Support for GMOD as a default service option selection.
@@ -431,7 +440,7 @@ the response from the server `GET` endpoint.
 * Changed 2FA login process to be more secure. Previously authentication checking happened on the 2FA post page, now it happens prior and is passed along to the 2FA page to avoid storing any credentials.
 
 ### Added
-* Connector error messages due to permissions are now rendered correctly in the UI rather than causing a silent failure.
+* Socketio error messages due to permissions are now rendered correctly in the UI rather than causing a silent failure.
 
 ## v0.7.0-beta.1 (Derelict Dermodactylus)
 ### Added
diff --git a/public/themes/pterodactyl/js/admin/new-server.js b/public/themes/pterodactyl/js/admin/new-server.js
index 7416451d..cda0d5cf 100644
--- a/public/themes/pterodactyl/js/admin/new-server.js
+++ b/public/themes/pterodactyl/js/admin/new-server.js
@@ -153,6 +153,12 @@ function updateAdditionalAllocations() {
 }
 
 function initUserIdSelect(data) {
+    function escapeHtml(str) {
+        var div = document.createElement('div');
+        div.appendChild(document.createTextNode(str));
+        return div.innerHTML;
+    }
+
     $('#pUserId').select2({
         ajax: {
             url: '/admin/users/accounts.json',
@@ -176,28 +182,27 @@ function initUserIdSelect(data) {
         data: data,
         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"> \
-                        <span class="username"> \
-                            <a href="#">' + data.name_first + ' ' + data.name_last +'</a> \
-                        </span> \
-                        <span class="description"><strong>' + data.email + '</strong> - ' + data.username + '</span> \
-                    </div>';
+                <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="#">' + escapeHtml(data.name_first) + ' ' + escapeHtml(data.name_last) +'</a> \
+                </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"> \
-                        </span> \
-                        <span style="padding-left:5px;"> \
-                            ' + data.name_first + ' ' + data.name_last + ' (<strong>' + data.email + '</strong>) \
-                        </span> \
-                    </div>';
+                <span> \
+                    <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;"> \
+                    ' + escapeHtml(data.name_first) + ' ' + escapeHtml(data.name_last) + ' (<strong>' + escapeHtml(data.email) + '</strong>) \
+                </span> \
+            </div>';
         }
+
     });
 }
diff --git a/resources/views/admin/servers/view/details.blade.php b/resources/views/admin/servers/view/details.blade.php
index 38c3a487..a3d6be62 100644
--- a/resources/views/admin/servers/view/details.blade.php
+++ b/resources/views/admin/servers/view/details.blade.php
@@ -66,6 +66,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: '/admin/users/accounts.json',
@@ -85,14 +91,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) {
@@ -108,10 +114,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>';
         }