You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cloudstack.apache.org by sh...@apache.org on 2026/08/18 13:55:55 UTC

(cloudstack) 21/23: Escape HTML in username, VM display name and diagnostics output

This is an automated email from the ASF dual-hosted git repository.

shwstppr pushed a commit to branch 4.22
in repository https://gitbox.apache.org/repos/asf/cloudstack.git

commit 2a027330579f85c4bf8939dcf1986bfe43571161
Author: Pearl Dsilva <pe...@gmail.com>
AuthorDate: Fri Jul 3 09:56:36 2026 -0400

    Escape HTML in username, VM display name and diagnostics output
---
 ui/src/config/section/compute.js         | 3 ++-
 ui/src/config/section/infra/routers.js   | 3 ++-
 ui/src/config/section/infra/systemVms.js | 3 ++-
 ui/src/config/section/network.js         | 3 ++-
 ui/src/config/section/user.js            | 3 ++-
 ui/src/utils/util.js                     | 9 +++++++++
 6 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/ui/src/config/section/compute.js b/ui/src/config/section/compute.js
index d8605471d19..10bf0a3973a 100644
--- a/ui/src/config/section/compute.js
+++ b/ui/src/config/section/compute.js
@@ -18,6 +18,7 @@
 import { shallowRef, defineAsyncComponent } from 'vue'
 import store from '@/store'
 import { isZoneCreated } from '@/utils/zone'
+import { escapeHtml } from '@/utils/util'
 
 export default {
   name: 'compute',
@@ -376,7 +377,7 @@ export default {
           show: (record) => { return record.hypervisor !== 'External' && ['Stopped'].includes(record.state) && record.passwordenabled },
           response: (result) => {
             return {
-              message: result.virtualmachine && result.virtualmachine.password ? `The password of VM <b>${result.virtualmachine.displayname}</b> is <b>${result.virtualmachine.password}</b>` : null,
+              message: result.virtualmachine && result.virtualmachine.password ? `The password of VM <b>${escapeHtml(result.virtualmachine.displayname)}</b> is <b>${result.virtualmachine.password}</b>` : null,
               copybuttontext: result.virtualmachine.password ? 'label.copy.password' : null,
               copytext: result.virtualmachine.password ? result.virtualmachine.password : null
             }
diff --git a/ui/src/config/section/infra/routers.js b/ui/src/config/section/infra/routers.js
index e52bcd7a829..6079f4a44f2 100644
--- a/ui/src/config/section/infra/routers.js
+++ b/ui/src/config/section/infra/routers.js
@@ -17,6 +17,7 @@
 
 import { shallowRef, defineAsyncComponent } from 'vue'
 import store from '@/store'
+import { escapeHtml } from '@/utils/util'
 
 export default {
   name: 'router',
@@ -226,7 +227,7 @@ export default {
           options: ['ping', 'ping6', 'traceroute', 'traceroute6', 'arping']
         }
       },
-      response: (result) => { return result && result.diagnostics ? `<strong>Output</strong>:<br/>${result.diagnostics.stdout}<br/><strong>Error</strong>: ${result.diagnostics.stderr}<br/><strong>Exit Code</strong>: ${result.diagnostics.exitcode}` : 'Invalid response' }
+      response: (result) => { return result && result.diagnostics ? `<strong>Output</strong>:<br/>${escapeHtml(result.diagnostics.stdout)}<br/><strong>Error</strong>: ${escapeHtml(result.diagnostics.stderr)}<br/><strong>Exit Code</strong>: ${result.diagnostics.exitcode}` : 'Invalid response' }
     },
     {
       api: 'getDiagnosticsData',
diff --git a/ui/src/config/section/infra/systemVms.js b/ui/src/config/section/infra/systemVms.js
index 6e135ccdd36..9a9910a6610 100644
--- a/ui/src/config/section/infra/systemVms.js
+++ b/ui/src/config/section/infra/systemVms.js
@@ -17,6 +17,7 @@
 
 import { shallowRef, defineAsyncComponent } from 'vue'
 import store from '@/store'
+import { escapeHtml } from '@/utils/util'
 
 export default {
   name: 'systemvm',
@@ -145,7 +146,7 @@ export default {
           options: ['ping', 'traceroute', 'arping']
         }
       },
-      response: (result) => { return result && result.diagnostics ? `<strong>Output</strong>:<br/>${result.diagnostics.stdout}<br/><strong>Error</strong>: ${result.diagnostics.stderr}<br/><strong>Exit Code</strong>: ${result.diagnostics.exitcode}` : 'Invalid response' }
+      response: (result) => { return result && result.diagnostics ? `<strong>Output</strong>:<br/>${escapeHtml(result.diagnostics.stdout)}<br/><strong>Error</strong>: ${escapeHtml(result.diagnostics.stderr)}<br/><strong>Exit Code</strong>: ${result.diagnostics.exitcode}` : 'Invalid response' }
     },
     {
       api: 'getDiagnosticsData',
diff --git a/ui/src/config/section/network.js b/ui/src/config/section/network.js
index acc7424c9f0..f59f6e6d15b 100644
--- a/ui/src/config/section/network.js
+++ b/ui/src/config/section/network.js
@@ -20,6 +20,7 @@ import store from '@/store'
 import tungsten from '@/assets/icons/tungsten.svg?inline'
 import { isAdmin } from '@/role'
 import { isZoneCreated } from '@/utils/zone'
+import { escapeHtml } from '@/utils/util'
 
 export default {
   name: 'network',
@@ -681,7 +682,7 @@ export default {
           show: (record) => { return ['Stopped'].includes(record.state) && record.passwordenabled },
           response: (result) => {
             return {
-              message: result.virtualmachine && result.virtualmachine.password ? `The password of VM <b>${result.virtualmachine.displayname}</b> is <b>${result.virtualmachine.password}</b>` : null,
+              message: result.virtualmachine && result.virtualmachine.password ? `The password of VM <b>${escapeHtml(result.virtualmachine.displayname)}</b> is <b>${result.virtualmachine.password}</b>` : null,
               copybuttontext: result.virtualmachine.password ? 'label.copy.password' : null,
               copytext: result.virtualmachine.password ? result.virtualmachine.password : null
             }
diff --git a/ui/src/config/section/user.js b/ui/src/config/section/user.js
index 49b603f850b..386d02dcb6c 100644
--- a/ui/src/config/section/user.js
+++ b/ui/src/config/section/user.js
@@ -18,6 +18,7 @@
 import { shallowRef, defineAsyncComponent } from 'vue'
 import store from '@/store'
 import { i18n } from '@/locales'
+import { escapeHtml } from '@/utils/util'
 
 export default {
   name: 'accountuser',
@@ -132,7 +133,7 @@ export default {
       api: 'lockUser',
       icon: 'LockOutlined',
       label: 'label.action.lock.user',
-      message: (record) => ['message.lock.user', { user: record.username }],
+      message: (record) => ['message.lock.user', { user: escapeHtml(record.username) }],
       successMessage: (record) => ['message.lock.user.success', { user: record.username }],
       dataView: true,
       popup: true,
diff --git a/ui/src/utils/util.js b/ui/src/utils/util.js
index 3c51096ac53..a6dd2bd689c 100644
--- a/ui/src/utils/util.js
+++ b/ui/src/utils/util.js
@@ -64,6 +64,15 @@ export function removeLoadingAnimate (id = '', timeout = 1500) {
   }, timeout)
 }
 
+export function escapeHtml (value) {
+  return String(value)
+    .replace(/&/g, '&amp;')
+    .replace(/</g, '&lt;')
+    .replace(/>/g, '&gt;')
+    .replace(/"/g, '&quot;')
+    .replace(/'/g, '&#039;')
+}
+
 export function sanitizeReverse (value) {
   return value
     .replace(/&amp;/g, '&')