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

Language: Python
Severity: High
CWE: CWE-1333

## Source
9

## Flow
9-11

## Sink
11

## 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 = re.compile(r'^(([a-zA-Z0-9]+)*)+:([0-9]+)*$')
    if pattern.match(device_id):
        return jsonify({'status': 'valid', 'device': device_id})
    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
```python
import re
from flask import Flask, request, jsonify

app = Flask(__name__)

# Pre-compiled safe regex pattern without nested quantifiers
DEVICE_ID_PATTERN = re.compile(r'^[a-zA-Z0-9]+(?:\.[a-zA-Z0-9]+)*:[0-9]+$')

# Maximum allowed length for device identifiers
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 check 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': device_id})
    return jsonify({'status': 'invalid'}), 400
```
