{"uuid": "cfd2dc7d-bbcb-4360-a91c-640cc6d99179", "vulnerability_lookup_origin": "1a89b78e-f703-45f3-bb86-59eb712668bd", "author": "9f56dd64-161d-43a6-b9c3-555944290a09", "vulnerability": "GHSA-v5r2-qh84-fjx5", "type": "seen", "source": "https://gist.github.com/nicolargo/c90ae7e9f35657ec0b51575e2c68667c", "content": "#\n# Fix for GHSA-v5r2-qh84-fjx5 / Command Injection via KVM/QEMU VM Domain Names\n#\n# Applies to: glances/plugins/vms/engines/virsh.py (develop branch, 4.x line)\n# Auto-forwarded to: develop-v5\n#\n# Strategy: replace f-string interpolation + secure_popen() with subprocess.run()\n# and an explicit argv list for the two call sites that interpolate the\n# attacker-controllable `domain` value. The other call sites in this file\n# (update_version, update_domains) use static commands and are not touched, to\n# keep the patch minimal and reviewable. They will be migrated as part of the\n# v5 refactor that retires secure_popen() entirely.\n#\n\n--- a/glances/plugins/vms/engines/virsh.py\n+++ b/glances/plugins/vms/engines/virsh.py\n@@ -8,8 +8,9 @@\n \"\"\"Virsh (QEMU/KVM) Extension unit for Glances' Vms plugin.\"\"\"\n\n import os\n import re\n+from subprocess import TimeoutExpired, run\n from functools import cache\n from typing import Any\n\n from glances.globals import nativestr\n from glances.plugins.vms.engines import VmsExtension\n from glances.secure import secure_popen\n@@ -19,8 +20,11 @@\n VIRSH_PATH = '/usr/bin/virsh'\n VIRSH_VERSION_OPTIONS = 'version'\n VIRSH_INFO_OPTIONS = 'list --all'\n-VIRSH_DOMAIN_STATS_OPTIONS = 'domstats --nowait'\n-VIRSH_DOMAIN_TITLE_OPTIONS = 'desc --title'\n+# Call sites that interpolate a user-controllable domain name use the argv-list\n+# variants below (see update_stats, update_title) to avoid command injection\n+# (GHSA-v5r2-qh84-fjx5).\n+VIRSH_DOMAIN_STATS_ARGS = ['domstats', '--nowait']\n+VIRSH_DOMAIN_TITLE_ARGS = ['desc', '--title']\n+VIRSH_SUBPROCESS_TIMEOUT = 5\n import_virsh_error_tag = not os.path.exists(VIRSH_PATH) or not os.access(VIRSH_PATH, os.X_OK)\n\n\n@@ -182,7 +186,18 @@ class VmExtension(VmsExtension):\n         #   vm.mmu_pte_write.sum=0\n         #   vm.remote_tlb_flush_requests.sum=609455\n-        ret_cmd = secure_popen(f'{VIRSH_PATH} {VIRSH_DOMAIN_STATS_OPTIONS} {domain}')\n+        # SECURITY: pass `domain` as a separate argv element rather than\n+        # interpolating into a string consumed by secure_popen(). secure_popen\n+        # splits its input on '&amp;&amp;', '|', and '&gt;', so a domain name containing\n+        # any of those would inject a second process (GHSA-v5r2-qh84-fjx5).\n+        try:\n+            result = run(\n+                [VIRSH_PATH, *VIRSH_DOMAIN_STATS_ARGS, domain],\n+                capture_output=True, text=True, timeout=VIRSH_SUBPROCESS_TIMEOUT,\n+            )\n+        except (OSError, TimeoutExpired):\n+            return {}\n+        ret_cmd = result.stdout if result.returncode == 0 else ''\n\n         try:\n             # Ignore first line (domain name already know) and last line (empty)\n             lines = ret_cmd.splitlines()[1:-1]\n@@ -201,7 +216,16 @@ class VmExtension(VmsExtension):\n     def update_title(self, domain):\n         # \u276f virsh desc --title Kali_Linux_2024\n         # Kali Linux 2024\n-        ret_cmd = secure_popen(f'{VIRSH_PATH} {VIRSH_DOMAIN_TITLE_OPTIONS} {domain}')\n+        # SECURITY: see update_stats() above (GHSA-v5r2-qh84-fjx5).\n+        try:\n+            result = run(\n+                [VIRSH_PATH, *VIRSH_DOMAIN_TITLE_ARGS, domain],\n+                capture_output=True, text=True, timeout=VIRSH_SUBPROCESS_TIMEOUT,\n+            )\n+        except (OSError, TimeoutExpired):\n+            return ''\n+        ret_cmd = result.stdout if result.returncode == 0 else ''\n\n         return ret_cmd.rstrip()", "creation_timestamp": "2026-05-17T10:05:05.000000Z"}