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

Language: Python
Severity: High
CWE: CWE-1333

## Source
8

## Flow
8-9-10

## Sink
10

## 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_serial():
    serial_number = request.json.get('device_serial', '')
    pattern = r'^(([A-Z0-9]+)*-)*([A-Z0-9]+)+$'
    if re.match(pattern, serial_number):
        return jsonify({'status': 'valid', 'serial': serial_number})
    return jsonify({'status': 'invalid'}), 400
```

## Explanation

The code uses a regex pattern with nested quantifiers (([A-Z0-9]+)*-)*([A-Z0-9]+)+ that causes catastrophic backtracking when matching fails. An attacker can submit a specially crafted device serial number that forces the regex engine into exponential time complexity, leading to CPU exhaustion and denial of service.

## Remediation

The fix replaces the vulnerable regex pattern that had nested quantifiers `(([A-Z0-9]+)*-)*([A-Z0-9]+)+` with a safe linear-time pattern `^[A-Z0-9]+(-[A-Z0-9]+)*$` that matches the same valid serial number format (alphanumeric segments separated by hyphens) without any ambiguous quantifier nesting. Additionally, an input length check of 128 characters is added as a defense-in-depth measure to prevent excessively long inputs from being processed.

## 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_serial():
    serial_number = request.json.get('device_serial', '')
    
    # Input length limit to prevent abuse
    if not serial_number or len(serial_number) > 128:
        return jsonify({'status': 'invalid'}), 400
    
    # Safe regex without nested quantifiers - matches segments of alphanumeric chars separated by hyphens
    pattern = r'^[A-Z0-9]+(-[A-Z0-9]+)*$'
    if re.match(pattern, serial_number):
        return jsonify({'status': 'valid', 'serial': serial_number})
    return jsonify({'status': 'invalid'}), 400
```
