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

Language: Python
Severity: High
CWE: CWE-1333

## Source
9

## Flow
9-12

## Sink
12

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

app = Flask(__name__)

@app.route('/api/iot/device/validate', methods=['POST'])
def validate_device_identifier():
    device_id = request.json.get('device_identifier', '')
    pattern = r'^([a-zA-Z0-9]+)*-([a-zA-Z0-9]+)*-([a-zA-Z0-9]+)*$'
    
    if re.match(pattern, device_id):
        return jsonify({
            'status': 'valid',
            'device_id': device_id,
            'registered': True
        }), 200
    else:
        return jsonify({
            'status': 'invalid',
            'error': 'Device identifier format incorrect'
        }), 400
```

## Explanation

The regex pattern uses nested quantifiers `([a-zA-Z0-9]+)*` which causes catastrophic backtracking when matching fails. Untrusted input from request.json flows directly to re.match() without sanitization or timeout, allowing attackers to cause exponential time complexity and DoS.

## Remediation

The fix removes the nested quantifiers `([a-zA-Z0-9]+)*` which caused catastrophic backtracking and replaces them with simple `[a-zA-Z0-9]+` character class quantifiers that match the same valid inputs without exponential time complexity. Additionally, an input length check (max 128 characters) is added as a defense-in-depth measure to limit processing time on any input.

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

app = Flask(__name__)

@app.route('/api/iot/device/validate', methods=['POST'])
def validate_device_identifier():
    device_id = request.json.get('device_identifier', '')
    
    # Limit input length to prevent abuse
    if not device_id or len(device_id) > 128:
        return jsonify({
            'status': 'invalid',
            'error': 'Device identifier format incorrect'
        }), 400
    
    # Fixed regex: removed nested quantifiers that caused catastrophic backtracking
    # Each group now uses a single quantifier [a-zA-Z0-9]+ without wrapping in ()+*
    pattern = r'^[a-zA-Z0-9]+-[a-zA-Z0-9]+-[a-zA-Z0-9]+$'
    
    if re.match(pattern, device_id):
        return jsonify({
            'status': 'valid',
            'device_id': device_id,
            'registered': True
        }), 200
    else:
        return jsonify({
            'status': 'invalid',
            'error': 'Device identifier format incorrect'
        }), 400
```
