Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion avm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -4571,4 +4571,6 @@ def request_response_to_xml(request):
"""
Parse request response to element object
"""
return ET.fromstring(request.content)
# added parser with resolve_entities option in response to CVE-2026-41066
xmlparser = ET.XMLParser(resolve_entities='internal')
return ET.fromstring(request.content, parser=xmlparser)
9 changes: 7 additions & 2 deletions avm/tr064/action.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ def __init__(self, xml, auth, base_url, name, service_type, service_id, control_
self.namespaces = IGD_SERVICE_NAMESPACE if 'igd' in description_file else TR064_SERVICE_NAMESPACE
self.control_namespace = IGD_CONTROL_NAMESPACE if 'igd' in description_file else TR064_CONTROL_NAMESPACE

# added parser with resolve_entities option in response to CVE-2026-41066
self._xmlparser = ET.XMLParser(resolve_entities='internal')

ET.register_namespace('s', 'http://schemas.xmlsoap.org/soap/envelope/')
ET.register_namespace('h', 'http://soap-authentication.org/digest/2001/10/')

Expand Down Expand Up @@ -92,7 +95,8 @@ def __call__(self, **kwargs):
verify=self.verify)
if request.status_code != 200:
try:
xml = ET.parse(BytesIO(request.content))
# added parser option in response to CVE-2026-41066
xml = ET.parse(BytesIO(request.content), parser=self._xmlparser)
except Exception:
return request.status_code
try:
Expand All @@ -104,7 +108,8 @@ def __call__(self, **kwargs):
return error_code if error_code is not None else request.status_code

# Translate response and prepare dict
xml = ET.parse(BytesIO(request.content))
# added parser option in response to CVE-2026-41066
xml = ET.parse(BytesIO(request.content), parser=self._xmlparser)
response = AttributeDict()
for arg in list(xml.find('.//{{{}}}{}Response'.format(self.service_type, self.name))):
name = self.out_arguments[arg.tag]
Expand Down
4 changes: 3 additions & 1 deletion avm/tr064/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,9 @@ def _fetch_devices(self, description_file='tr64desc.xml'):
# request = requests.get(f'{self.base_url}/{description_file}', verify=self.verify)

if request.status_code == 200:
xml = ET.parse(BytesIO(request.content))
# added parser with resolve_entities option in response to CVE-2026-41066
xmlparser = ET.XMLParser(resolve_entities='internal')
xml = ET.parse(BytesIO(request.content), parser=xmlparser)

for device in xml.findall('.//device', namespaces=self.namespaces):
name = device.findtext('deviceType', namespaces=self.namespaces).split(':')[-2]
Expand Down
6 changes: 5 additions & 1 deletion avm/tr064/service.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ def __init__(self, auth, base_url, service_type, service_id, scpdurl, control_ur
self.description_file = description_file
self.namespaces = IGD_SERVICE_NAMESPACE if 'igd' in description_file else TR064_SERVICE_NAMESPACE

# added parser with resolve_entities option in response to CVE-2026-41066
self._xmlparser = ET.XMLParser(resolve_entities='internal')

def __getattr__(self, name):
if name not in self.actions:
self._fetch_actions(self.scpdurl)
Expand All @@ -40,7 +43,8 @@ def _fetch_actions(self, scpdurl):
"""Fetch action description."""
request = requests.get('{0}{1}'.format(self.base_url, scpdurl), verify=self.verify)
if request.status_code == 200:
xml = ET.parse(BytesIO(request.content))
# added parser option in response to CVE-2026-41066
xml = ET.parse(BytesIO(request.content), parser=self._xmlparser)

for action in xml.findall('./actionList/action', namespaces=self.namespaces):
name = action.findtext('name', namespaces=self.namespaces)
Expand Down
3 changes: 2 additions & 1 deletion sonos/soco/data_structures_entry.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,8 @@ def from_didl_string(string):
list: A list of one or more instances of `DidlObject` or a subclass
"""
items = []
parser = ET.XMLParser(recover=True, encoding="utf-8")
# added resolve_entities in response to CVE-2026-41066
parser = ET.XMLParser(recover=True, encoding="utf-8", resolve_entities='internal')
root = ET.fromstring(string.encode("utf-8"), parser=parser)
for elt in root:
if elt.tag.endswith("item") or elt.tag.endswith("container"):
Expand Down
3 changes: 2 additions & 1 deletion sonos/soco/zonegroupstate.py
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ def update_soco_instances(self, tree):

def normalize_zgs_xml(xml):
"""Normalize the ZoneGroupState payload and return an lxml ElementTree instance."""
parser = LXML.XMLParser(remove_blank_text=True) # pylint:disable=I1101
# added resolve_entities in response to CVE-2026-41066
parser = LXML.XMLParser(remove_blank_text=True, resolve_entities='internal') # pylint:disable=I1101
tree = LXML.fromstring(xml, parser) # pylint:disable=I1101
return ZGS_TRANSFORM(tree)
27 changes: 19 additions & 8 deletions yamaha/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ def __init__(self, smarthome):
self.mcast_buffer = 1024
self.mcast_service = "urn:schemas-yamaha-com:service:X_YamahaRemoteControl:1"

# added parser with resolve_entities option in response to CVE-2026-41066
self._xmlparser = etree.XMLParser(resolve_entities='internal')

# On initialization error use:
if not REQUIRED_PACKAGE_IMPORTED:
self._init_complete = False
Expand Down Expand Up @@ -194,7 +197,8 @@ def _event_notify(self, value, cmd='PUT'):
notice.text = 'On'
else:
notice.text = 'Off'
tree = etree.ElementTree(root)
# added parser option in response to CVE-2026-41066
tree = etree.ElementTree(root, parser=self._xmlparser)
return self._return_document(tree)

def _power(self, value, cmd='PUT'):
Expand All @@ -209,7 +213,8 @@ def _power(self, value, cmd='PUT'):
power.text = 'Standby'
elif value == 'GetParam':
power.text = value
tree = etree.ElementTree(root)
# added parser option in response to CVE-2026-41066
tree = etree.ElementTree(root, parser=self._xmlparser)
return self._return_document(tree)

def _input(self, value, cmd='PUT'):
Expand All @@ -219,7 +224,8 @@ def _input(self, value, cmd='PUT'):
input = etree.SubElement(system, 'Input')
input_sel = etree.SubElement(input, 'Input_Sel')
input_sel.text = value
tree = etree.ElementTree(root)
# added parser option in response to CVE-2026-41066
tree = etree.ElementTree(root, parser=self._xmlparser)
return self._return_document(tree)

def _volume(self, value, cmd='PUT'):
Expand All @@ -237,7 +243,8 @@ def _volume(self, value, cmd='PUT'):
exponent.text = '1'
unit = etree.SubElement(level, 'Unit')
unit.text = 'dB'
tree = etree.ElementTree(root)
# added parser option in response to CVE-2026-41066
tree = etree.ElementTree(root, parser=self._xmlparser)
return self._return_document(tree)

def _mute(self, value, cmd='PUT'):
Expand All @@ -252,7 +259,8 @@ def _mute(self, value, cmd='PUT'):
mute.text = 'Off'
elif value == 'GetParam':
mute.text = value
tree = etree.ElementTree(root)
# added parser option in response to CVE-2026-41066
tree = etree.ElementTree(root, parser=self._xmlparser)
return self._return_document(tree)

def _validate_inputs(self):
Expand All @@ -261,7 +269,8 @@ def _validate_inputs(self):
system = etree.SubElement(root, 'System')
state = etree.SubElement(system, 'Config')
state.text = 'GetParam'
tree = etree.ElementTree(root)
# added parser option in response to CVE-2026-41066
tree = etree.ElementTree(root, parser=self._xmlparser)
return self._return_document(tree)

def _get_state(self):
Expand All @@ -270,7 +279,8 @@ def _get_state(self):
system = etree.SubElement(root, 'Main_Zone')
state = etree.SubElement(system, 'Basic_Status')
state.text = 'GetParam'
tree = etree.ElementTree(root)
# added parser option in response to CVE-2026-41066
tree = etree.ElementTree(root, parser=self._xmlparser)
return self._return_document(tree)

def _get_value(self, notify_cmd, yamaha_host):
Expand All @@ -292,7 +302,8 @@ def _get_value(self, notify_cmd, yamaha_host):

def _return_value(self, state, cmd):
try:
tree = etree.parse(StringIO(state))
# added parser option in response to CVE-2026-41066
tree = etree.parse(StringIO(state), parser=self._xmlparser)
except Exception:
return "Invalid data received"
if cmd == 'input':
Expand Down
Loading