# Regex DoS via Catastrophic Backtracking in re.search()

Language: Python
Severity: High
CWE: CWE-1333

## Source
8

## Flow
8-10

## Sink
10

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

app = Flask(__name__)

@app.route('/api/iot/validate_device_id', methods=['POST'])
def validate_iot_device_identifier():
    device_id = request.json.get('device_identifier', '')
    pattern = r'^(([a-zA-Z0-9]+)+)+@iot\.device$'
    if re.search(pattern, device_id):
        return jsonify({'status': 'valid', 'device_id': device_id})
    return jsonify({'status': 'invalid'}), 400
```

## Explanation

The regex pattern `^(([a-zA-Z0-9]+)+)+@iot\.device$` contains nested quantifiers that cause catastrophic backtracking when matching fails. Untrusted input from `request.json.get('device_identifier')` is directly passed to `re.search()` without input validation or timeout, allowing an attacker to craft malicious device IDs that exponentially increase regex evaluation time, leading to denial of service.

## Remediation

The fix removes the nested quantifiers `(([a-zA-Z0-9]+)+)+` that caused catastrophic backtracking and replaces them with a simple `[a-zA-Z0-9]+` which matches one or more alphanumeric characters without any ambiguity. Additionally, input length validation is enforced before regex evaluation, and the pattern is pre-compiled using `re.compile()` with `match()` instead of `search()` for better performance and correctness with the `^` anchor.

## Secure Code
```python
import re
from flask import Flask, request, jsonify

app = Flask(__name__)

# Pre-compiled safe regex without nested quantifiers
DEVICE_ID_PATTERN = re.compile(r'^[a-zA-Z0-9]+@iot\.device$')

MAX_DEVICE_ID_LENGTH = 256

@app.route('/api/iot/validate_device_id', methods=['POST'])
def validate_iot_device_identifier():
    device_id = request.json.get('device_identifier', '')
    
    # Input length validation to prevent abuse
    if not device_id or len(device_id) > MAX_DEVICE_ID_LENGTH:
        return jsonify({'status': 'invalid'}), 400
    
    if DEVICE_ID_PATTERN.match(device_id):
        return jsonify({'status': 'valid', 'device_id': device_id})
    return jsonify({'status': 'invalid'}), 400
```
