{"title":"Regex Denial of Service (ReDoS) via Catastrophic Backtracking","language":"Python","severity":"High","cwe":"CWE-1333","source_lines":[6],"flow_lines":[6,7,8],"sink_lines":[8],"vulnerable_code":"import re\nfrom flask import Flask, request, jsonify\napp = Flask(__name__)\n@app.route('/api/iot/device/register', methods=['POST'])\ndef register_device():\n    dev_payload = request.json.get('device_firmware_version', '')\n    pattern = re.compile(r'^(([a-zA-Z0-9]+\\.)+[a-zA-Z0-9]+\\-?)+$')\n    if not pattern.match(dev_payload):\n        return jsonify({'status': 'error', 'msg': 'Invalid firmware version format'}), 400\n    return jsonify({'status': 'ok', 'registered': dev_payload}), 200","explanation":"The untrusted input `dev_payload` is read from the HTTP request body on line 6 and directly passed to `pattern.match()` on line 8. The regex pattern on line 7 contains nested quantifiers `(([a-zA-Z0-9]+\\.)+[a-zA-Z0-9]+\\-?)+` which cause catastrophic backtracking when a crafted input is provided, leading to exponential CPU time consumption and a Denial of Service.","remediation":"The fix replaces the vulnerable regex that had nested quantifiers `(([a-zA-Z0-9]+\\.)+[a-zA-Z0-9]+\\-?)+` with a safe, linear-time regex `^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*(?:-[a-zA-Z0-9]+)?$` that uses only non-nested quantifiers and non-capturing groups, completely eliminating catastrophic backtracking. Additionally, an input length limit (128 characters) and a type check are enforced before regex evaluation to provide defense-in-depth against oversized or malformed inputs.","secure_code":"import re\nfrom flask import Flask, request, jsonify\napp = Flask(__name__)\n\nMAX_FIRMWARE_VERSION_LENGTH = 128\n# Linear-time regex: no nested quantifiers, matches patterns like \"abc.def.ghi\" or \"abc.def.ghi-1\"\nFIRMWARE_VERSION_PATTERN = re.compile(r'^[a-zA-Z0-9]+(?:\\.[a-zA-Z0-9]+)*(?:-[a-zA-Z0-9]+)?$')\n\n@app.route('/api/iot/device/register', methods=['POST'])\ndef register_device():\n    dev_payload = request.json.get('device_firmware_version', '')\n    if not isinstance(dev_payload, str) or len(dev_payload) > MAX_FIRMWARE_VERSION_LENGTH:\n        return jsonify({'status': 'error', 'msg': 'Invalid firmware version format'}), 400\n    if not FIRMWARE_VERSION_PATTERN.match(dev_payload):\n        return jsonify({'status': 'error', 'msg': 'Invalid firmware version format'}), 400\n    return jsonify({'status': 'ok', 'registered': dev_payload}), 200"}