1
0
Fork 0
mirror of https://github.com/retspen/webvirtcloud synced 2026-11-08 11:16:47 +00:00

fix(instances): restrict disk views to superusers and validate disk input

Disk and media mutation views (add_new_vol, add_existing_vol, edit_volume,
delete_vol, detach_vol, add_cdrom, detach_cdrom, mount_iso, unmount_iso)
were reachable by any owner with is_change, although the UI only shows the
Disk tab to superusers. An ordinary user could delete any volume on the
compute or attach arbitrary host files to their VM.

- Require superuser for all disk/media views, matching the UI.
- delete_vol resolves the volume from the VM's own disk XML by dev and
  ignores the posted storage/name.
- Validate bus/cache/io/discard/detect_zeroes against the hypervisor's
  lists, and format/serial/volume name against strict patterns.
- add_existing_vol only accepts volumes that exist in the selected pool.
- edit_volume only accepts a dev attached to the VM.
- vrtManager: escape all values interpolated into disk XML and pass dev
  as an XPath variable in detach_disk/edit_disk.
This commit is contained in:
catborise 2026-10-02 17:11:12 +03:00
parent b1ab1d47fa
commit 8a3ee13b41
5 changed files with 573 additions and 179 deletions

View file

@ -760,6 +760,15 @@ class wvmInstance(wvmConnect):
discard_mode=None,
detect_zeroes_mode=None,
):
# Every value below is interpolated into disk XML; escape them all so
# a quote in a path, serial or option cannot inject XML.
esc = util.xml_escape
target_dev, source, target_bus = esc(target_dev), esc(source), esc(target_bus)
disk_type, disk_device = esc(disk_type), esc(disk_device)
driver_name, format_type = esc(driver_name), esc(format_type)
serial = esc(serial)
cache_mode, io_mode = esc(cache_mode), esc(io_mode)
discard_mode, detect_zeroes_mode = esc(discard_mode), esc(detect_zeroes_mode)
additionals = ""
if (
@ -822,7 +831,7 @@ class wvmInstance(wvmConnect):
def detach_disk(self, target_dev):
tree = etree.fromstring(self._XMLDesc(0))
disk_el = tree.xpath("./devices/disk/target[@dev='{}']".format(target_dev))[
disk_el = tree.xpath("./devices/disk/target[@dev=$dev]", dev=target_dev)[
0
].getparent()
xml_disk = etree.tostring(disk_el).decode()
@ -850,7 +859,7 @@ class wvmInstance(wvmConnect):
detect_zeroes_mode,
):
tree = etree.fromstring(self._XMLDesc(0))
disk_el = tree.xpath("./devices/disk/target[@dev='{}']".format(target_dev))[
disk_el = tree.xpath("./devices/disk/target[@dev=$dev]", dev=target_dev)[
0
].getparent()
old_disk_type = disk_el.get("type")
@ -858,6 +867,15 @@ class wvmInstance(wvmConnect):
old_driver_name = disk_el.xpath("driver/@name")[0]
old_target_bus = disk_el.xpath("target/@bus")[0]
# Values below are interpolated into disk XML; escape them all.
esc = util.xml_escape
target_dev, source, target_bus = esc(target_dev), esc(source), esc(target_bus)
serial, format = esc(serial), esc(format)
cache_mode, io_mode = esc(cache_mode), esc(io_mode)
discard_mode, detect_zeroes_mode = esc(discard_mode), esc(detect_zeroes_mode)
old_disk_type, old_disk_device = esc(old_disk_type), esc(old_disk_device)
old_driver_name = esc(old_driver_name)
additionals = ""
if cache_mode is not None and cache_mode != "default":
additionals += f"cache='{cache_mode}' "