{"title":"Regex DoS via Catastrophic Backtracking","language":"Python","severity":"High","cwe":"CWE-1333","source_lines":[12],"flow_lines":[12,13,5,6],"sink_lines":[6],"vulnerable_code":"import re\nfrom flask import request, jsonify\n\ndef validate_iot_device_serial(serial_number):\n    pattern = r'^(IOT-([A-Z0-9]+-)*)+(DEVICE-[0-9]+)$'\n    if re.match(pattern, serial_number):\n        return jsonify({\"status\": \"valid\", \"serial\": serial_number})\n    return jsonify({\"status\": \"invalid\"})\n\n@app.route('/api/iot/register', methods=['POST'])\ndef register_device():\n    device_serial = request.json.get('serial_number', '')\n    return validate_iot_device_serial(device_serial)","explanation":"The regex pattern on line 5 contains nested quantifiers `(([A-Z0-9]+-)*)+(DEVICE-[0-9]+)` which causes catastrophic backtracking. When user-controlled input from line 12 is passed to re.match() on line 6, a crafted serial number with many repetitions but no match forces the regex engine into exponential time complexity, enabling a ReDoS attack that can freeze the application.","remediation":"The fix removes the nested quantifiers `(([A-Z0-9]+-)*)+ ` that caused catastrophic backtracking by replacing them with a single non-capturing group `(?:[A-Z0-9]+-)+` which matches one or more alphanumeric segments followed by a dash. Additionally, an input length check is added to provide defense-in-depth against excessively long inputs.","secure_code":"import re\nfrom flask import request, jsonify\n\nMAX_SERIAL_LENGTH = 256\n\ndef validate_iot_device_serial(serial_number):\n    if not serial_number or len(serial_number) > MAX_SERIAL_LENGTH:\n        return jsonify({\"status\": \"invalid\"})\n    \n    # Use a non-backtracking pattern that matches the expected format:\n    # IOT- followed by one or more segments of [A-Z0-9]+- then DEVICE-[0-9]+\n    pattern = r'^IOT-(?:[A-Z0-9]+-)+DEVICE-[0-9]+$'\n    if re.match(pattern, serial_number):\n        return jsonify({\"status\": \"valid\", \"serial\": serial_number})\n    return jsonify({\"status\": \"invalid\"})\n\n@app.route('/api/iot/register', methods=['POST'])\ndef register_device():\n    device_serial = request.json.get('serial_number', '')\n    return validate_iot_device_serial(device_serial)"}