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

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/validate_device_serial', methods=['POST'])
def validate_iot_device_serial():
    serial_num = request.json.get('device_serial', '')
    pattern = r'^(IOT-[A-Z0-9]+-)+(PROD|DEV|TEST)+$'
    if re.search(pattern, serial_num):
        return jsonify({'status': 'valid', 'device': serial_num})
    return jsonify({'status': 'invalid'}), 400
```

## Explanation

The regex pattern '^(IOT-[A-Z0-9]+-)+(PROD|DEV|TEST)+$' contains nested quantifiers that cause catastrophic backtracking. When re.search() processes a malformed serial like 'IOT-ABC-IOT-DEF-IOT-GHI-INVALID', the regex engine exponentially retries combinations, causing CPU exhaustion and denial of service.

## Remediation

The fix eliminates catastrophic backtracking by replacing the nested quantifiers `(IOT-[A-Z0-9]+-)+` with a non-capturing group having a bounded repetition `(?:IOT-[A-Z0-9]+-){1,10}`, and removes the unnecessary `+` after the suffix group `(PROD|DEV|TEST)`. Additionally, input length is capped at 128 characters to prevent excessively long inputs from reaching the regex engine, and `re.match()` is used instead of `re.search()` since the pattern is anchored.

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

app = Flask(__name__)

MAX_SERIAL_LENGTH = 128
SERIAL_PATTERN = re.compile(r'^(?:IOT-[A-Z0-9]+-){1,10}(?:PROD|DEV|TEST)$')

@app.route('/api/iot/validate_device_serial', methods=['POST'])
def validate_iot_device_serial():
    serial_num = request.json.get('device_serial', '')
    if not isinstance(serial_num, str) or len(serial_num) > MAX_SERIAL_LENGTH:
        return jsonify({'status': 'invalid'}), 400
    if SERIAL_PATTERN.match(serial_num):
        return jsonify({'status': 'valid', 'device': serial_num})
    return jsonify({'status': 'invalid'}), 400
```
