{"title":"XPath Injection via lxml.etree.XPath Predicate Construction","language":"Python","severity":"High","cwe":"CWE-643","source_lines":[3],"flow_lines":[3,4,5],"sink_lines":[4,5],"vulnerable_code":"from lxml import etree\n\ndef retrieve_iot_device_metrics(device_xml, sensor_id):\n    doc = etree.fromstring(device_xml)\n    query = f\"//device/sensor[@id='{sensor_id}']/metrics\"\n    xpath_eval = etree.XPath(query)\n    results = xpath_eval(doc)\n    if results:\n        return etree.tostring(results[0], encoding='unicode')\n    return None","explanation":"The sensor_id parameter is directly interpolated into an XPath query string using an f-string without sanitization. An attacker can inject malicious XPath expressions through sensor_id to bypass access controls, extract unauthorized data, or manipulate query logic.","remediation":"The fix uses lxml's built-in XPath variable substitution via the `$variable` syntax and passing the parameter as a keyword argument to `doc.xpath()`, which safely handles the value without allowing injection. Additionally, an input validation whitelist ensures that only expected characters are accepted in the sensor_id parameter, providing defense in depth.","secure_code":"from lxml import etree\nimport re\n\ndef retrieve_iot_device_metrics(device_xml, sensor_id):\n    # Validate sensor_id to only allow safe characters (alphanumeric, hyphens, underscores)\n    if not re.match(r'^[a-zA-Z0-9_\\-]+$', sensor_id):\n        raise ValueError(\"Invalid sensor_id: only alphanumeric characters, hyphens, and underscores are allowed\")\n    doc = etree.fromstring(device_xml)\n    query = \"//device/sensor[@id=$sensor_id]/metrics\"\n    results = doc.xpath(query, sensor_id=sensor_id)\n    if results:\n        return etree.tostring(results[0], encoding='unicode')\n    return None"}