{"title":"Regex DoS via Catastrophic Backtracking in re.compile()","language":"Python","severity":"High","cwe":"CWE-1333","source_lines":[9],"flow_lines":[9,11],"sink_lines":[11],"vulnerable_code":"import re\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\n\n@app.route('/api/iot/validate_device_id', methods=['POST'])\ndef validate_iot_device_identifier():\n    device_id = request.json.get('device_identifier', '')\n    pattern = re.compile(r'^(([a-zA-Z0-9]+)*)+:([0-9]+)*$')\n    if pattern.match(device_id):\n        return jsonify({'status': 'valid', 'device': device_id})\n    return jsonify({'status': 'invalid'}), 400","explanation":"The regex pattern contains nested quantifiers `(([a-zA-Z0-9]+)*)+` which causes catastrophic backtracking. When the untrusted device_id input contains many repetitive characters followed by a non-matching character, the regex engine explores exponential paths before failing, leading to CPU exhaustion and denial of service.","remediation":"The fix replaces the vulnerable regex pattern `^(([a-zA-Z0-9]+)*)+:([0-9]+)*$` containing nested quantifiers with a safe linear-time pattern `^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*:[0-9]+$` that matches one or more alphanumeric segments separated by dots followed by a colon and port number. Additionally, an input length limit of 256 characters is enforced before regex evaluation to provide defense-in-depth against any potential backtracking issues.","secure_code":"import re\nfrom flask import Flask, request, jsonify\n\napp = Flask(__name__)\n\n# Pre-compiled safe regex pattern without nested quantifiers\nDEVICE_ID_PATTERN = re.compile(r'^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*:[0-9]+$')\n\n# Maximum allowed length for device identifiers\nMAX_DEVICE_ID_LENGTH = 256\n\n@app.route('/api/iot/validate_device_id', methods=['POST'])\ndef validate_iot_device_identifier():\n    device_id = request.json.get('device_identifier', '')\n    \n    # Input length check to prevent abuse\n    if not device_id or len(device_id) > MAX_DEVICE_ID_LENGTH:\n        return jsonify({'status': 'invalid'}), 400\n    \n    if DEVICE_ID_PATTERN.match(device_id):\n        return jsonify({'status': 'valid', 'device': device_id})\n    return jsonify({'status': 'invalid'}), 400"}