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

fix(storages): Ceph and NFS pool values are escaped, host names may be long

create_storage_ceph and create_storage_netfs wrote the pool, host, user,
secret and format values into the pool XML unescaped and unchecked: the
page's patterns were the only guard, and a direct POST could break or
extend the XML. The values now go through util.xml_escape, as the
network code does. The page's host fields stopped at 20 characters, so
nfs01.storage.example.com could not be typed: they take a host name's
253; the Ceph pool and user fields lose the limit the server never had.
This commit is contained in:
catborise 2026-10-19 20:33:06 +03:00
parent 7f1780ad2d
commit eb828b364d
4 changed files with 39 additions and 4 deletions

View file

@ -120,19 +120,19 @@
<div class="row">
<label for="stg_rbd_ceph_user" class="col-sm-3 col-form-label">{% trans "Ceph User" %}</label>
<div class="col-sm-6">
<input id="stg_rbd_ceph_user" type="text" class="form-control" name="ceph_user" placeholder="libvirt" maxlength="20" required pattern="[a-zA-Z0-9\.\-_]+">
<input id="stg_rbd_ceph_user" type="text" class="form-control" name="ceph_user" placeholder="libvirt" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="row">
<label for="stg_rbd_ceph_pool" class="col-sm-3 col-form-label">{% trans "Ceph Pool" %}</label>
<div class="col-sm-6">
<input id="stg_rbd_ceph_pool" type="text" class="form-control" name="ceph_pool" placeholder="libvirt-pool" maxlength="20" required pattern="[a-zA-Z0-9\.\-_]+">
<input id="stg_rbd_ceph_pool" type="text" class="form-control" name="ceph_pool" placeholder="libvirt-pool" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="row">
<label for="stg_rbd_ceph_host" class="col-sm-3 col-form-label">{% trans "Ceph Host" %}</label>
<div class="col-sm-6">
<input id="stg_rbd_ceph_host" type="text" class="form-control" name="ceph_host" placeholder="mon0.ceph.host" maxlength="20" required pattern="[a-zA-Z0-9\.\-_]+">
<input id="stg_rbd_ceph_host" type="text" class="form-control" name="ceph_host" placeholder="mon0.ceph.host" maxlength="253" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="row">
@ -185,7 +185,7 @@
<div class="row">
<label for="stg_netfs_netfs_host" class="col-sm-3 col-form-label">{% trans "Hostname" %}</label>
<div class="col-sm-6">
<input id="stg_netfs_netfs_host" type="text" class="form-control" name="netfs_host" placeholder="nfs.example.com" maxlength="20" required pattern="[a-zA-Z0-9\.\-_]+">
<input id="stg_netfs_netfs_host" type="text" class="form-control" name="netfs_host" placeholder="nfs.example.com" maxlength="253" required pattern="[a-zA-Z0-9\.\-_]+">
</div>
</div>
<div class="row">

View file

@ -74,3 +74,6 @@ class PoolTargetTests(SimpleTestCase):
with self.subTest(form=field.xpath("string(ancestor::form//input[@name='stg_type']/@value)"), name=field.name):
self.assertIsNone(field.get("pattern"))
self.assertGreaterEqual(int(field.get("maxlength", 100)), 100)
for name in ("netfs_host", "ceph_host"): # a host name is up to 253 characters
with self.subTest(name=name):
self.assertGreaterEqual(int(page.xpath(f"//input[@name='{name}']/@maxlength")[0]), 253)

View file

@ -0,0 +1,30 @@
"""The host, pool, user and format values of a new Ceph or NFS pool go into
its XML as text, whatever characters they hold."""
from unittest.mock import patch
from django.test import SimpleTestCase
from lxml import etree
from vrtManager.storage import wvmStorages
ODD = "a'b\"c<d>&e"
class PoolXmlTests(SimpleTestCase):
def defined_xml(self, create, *args):
with patch("vrtManager.connection.connection_manager.get_connection") as get:
getattr(wvmStorages("host", "login", "pw", 1), create)(*args)
return etree.fromstring(get.return_value.storagePoolDefineXML.call_args.args[0])
def test_ceph_values_are_text(self):
pool = self.defined_xml("create_storage_ceph", "rbd", "pool1", ODD + "pool", ODD + "host", ODD + "user", ODD + "secret")
self.assertEqual(pool.findtext("source/name"), ODD + "pool")
self.assertEqual(pool.find("source/host").get("name"), ODD + "host")
self.assertEqual(pool.find("source/auth").get("username"), ODD + "user")
self.assertEqual(pool.find("source/auth/secret").get("uuid"), ODD + "secret")
def test_nfs_values_are_text(self):
pool = self.defined_xml("create_storage_netfs", "netfs", "pool1", ODD + "host", "/srv/nfs", ODD + "fmt", "/mnt/pool1")
self.assertEqual(pool.find("source/host").get("name"), ODD + "host")
self.assertEqual(pool.find("source/format").get("type"), ODD + "fmt")
self.assertEqual(pool.find("source/dir").get("path"), "/srv/nfs")

View file

@ -66,6 +66,7 @@ class wvmStorages(wvmConnect):
def create_storage_ceph(
self, stg_type, name, ceph_pool, ceph_host, ceph_user, secret
):
ceph_pool, ceph_host, ceph_user, secret = map(util.xml_escape, (ceph_pool, ceph_host, ceph_user, secret))
xml = f"""
<pool type='{stg_type}'>
<name>{name}</name>
@ -86,6 +87,7 @@ class wvmStorages(wvmConnect):
def create_storage_netfs(
self, stg_type, name, netfs_host, source, source_format, target
):
netfs_host, source_format = util.xml_escape(netfs_host), util.xml_escape(source_format)
xml = f"""
<pool type='{stg_type}'>
<name>{name}</name>