mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-09 11:46:49 +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:
parent
b1ab1d47fa
commit
8a3ee13b41
5 changed files with 573 additions and 179 deletions
|
|
@ -178,3 +178,67 @@ class TestXmlSafety(unittest.TestCase):
|
|||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
||||
|
||||
class TestDiskXmlEscaping(unittest.TestCase):
|
||||
def _instance(self):
|
||||
inst = wvmInstance.__new__(wvmInstance)
|
||||
inst.get_status = MagicMock(return_value=5) # shutoff
|
||||
inst.instance = MagicMock()
|
||||
return inst
|
||||
|
||||
def test_attach_disk_escapes_source_and_serial(self):
|
||||
inst = self._instance()
|
||||
inst.attach_disk(
|
||||
"vdb",
|
||||
"/images/a'/><disk type='file",
|
||||
target_bus="virtio",
|
||||
disk_type="file",
|
||||
serial="S1</serial><evil/>",
|
||||
)
|
||||
xml = inst.instance.attachDeviceFlags.call_args[0][0]
|
||||
self.assertIn("file='/images/a'/><disk type='file'", xml)
|
||||
self.assertIn("<serial>S1</serial><evil/></serial>", xml)
|
||||
self.assertNotIn("<evil/>", xml)
|
||||
|
||||
def test_edit_disk_escapes_source_and_serial(self):
|
||||
inst = self._instance()
|
||||
inst._XMLDesc = MagicMock(
|
||||
return_value=(
|
||||
"<domain><devices><disk type='file' device='disk'>"
|
||||
"<driver name='qemu' type='qcow2'/>"
|
||||
"<source file='/images/vm.qcow2'/>"
|
||||
"<target dev='vda' bus='virtio'/>"
|
||||
"</disk></devices></domain>"
|
||||
)
|
||||
)
|
||||
inst.edit_disk(
|
||||
"vda",
|
||||
"/images/b'/><x y='",
|
||||
False,
|
||||
False,
|
||||
"virtio",
|
||||
"S2<evil/>",
|
||||
"qcow2",
|
||||
"default",
|
||||
"default",
|
||||
"default",
|
||||
"default",
|
||||
)
|
||||
xml = inst.instance.updateDeviceFlags.call_args[0][0]
|
||||
self.assertIn("file='/images/b'/><x y=''", xml)
|
||||
self.assertNotIn("<evil/>", xml)
|
||||
|
||||
def test_detach_disk_does_not_evaluate_dev_as_xpath(self):
|
||||
inst = self._instance()
|
||||
inst._XMLDesc = MagicMock(
|
||||
return_value=(
|
||||
"<domain><devices><disk type='file' device='disk'>"
|
||||
"<source file='/images/vm.qcow2'/>"
|
||||
"<target dev='vda' bus='virtio'/>"
|
||||
"</disk></devices></domain>"
|
||||
)
|
||||
)
|
||||
with self.assertRaises(IndexError):
|
||||
inst.detach_disk("x' or '1'='1")
|
||||
inst.instance.detachDeviceFlags.assert_not_called()
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue