# Regex DoS via Catastrophic Backtracking

Language: Python
Severity: High
CWE: CWE-1333

## Source
12

## Flow
12-13-5-6

## Sink
6

## Vulnerable Code
```python
import re
from flask import request, jsonify

def validate_iot_device_serial(serial_number):
    pattern = r'^(IOT-([A-Z0-9]+-)*)+(DEVICE-[0-9]+)$'
    if re.match(pattern, serial_number):
        return jsonify({"status": "valid", "serial": serial_number})
    return jsonify({"status": "invalid"})

@app.route('/api/iot/register', methods=['POST'])
def register_device():
    device_serial = request.json.get('serial_number', '')
    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
```python
import re
from flask import request, jsonify

MAX_SERIAL_LENGTH = 256

def validate_iot_device_serial(serial_number):
    if not serial_number or len(serial_number) > MAX_SERIAL_LENGTH:
        return jsonify({"status": "invalid"})
    
    # Use a non-backtracking pattern that matches the expected format:
    # IOT- followed by one or more segments of [A-Z0-9]+- then DEVICE-[0-9]+
    pattern = r'^IOT-(?:[A-Z0-9]+-)+DEVICE-[0-9]+$'
    if re.match(pattern, serial_number):
        return jsonify({"status": "valid", "serial": serial_number})
    return jsonify({"status": "invalid"})

@app.route('/api/iot/register', methods=['POST'])
def register_device():
    device_serial = request.json.get('serial_number', '')
    return validate_iot_device_serial(device_serial)
```
