# Log Injection via Unsanitized Newline Characters in Python Logging

Language: Python
Severity: Medium
CWE: CWE-117

## Source
5-8

## Flow
5-9, 6-9, 7-9, 8-9

## Sink
9

## Vulnerable Code
```python
import logging
from flask import request, jsonify

logger = logging.getLogger('iot_device_manager')

def register_smart_device():
    device_id = request.json.get('device_id', 'unknown')
    device_type = request.json.get('device_type', 'generic')
    firmware_ver = request.json.get('firmware_version', 'N/A')
    owner_email = request.json.get('owner_email', '')
    logger.info(f"IoT Device Registration: ID={device_id}, Type={device_type}, Firmware={firmware_ver}, Owner={owner_email}")
    if device_id and device_type:
        return jsonify({"status": "registered", "device_id": device_id})
    return jsonify({"status": "failed", "reason": "missing parameters"}), 400
```

## Explanation

User-controlled input from request.json (device_id, device_type, firmware_ver, owner_email) is directly interpolated into a logging statement without sanitization. An attacker can inject newline characters (\n, \r) to forge log entries, potentially hiding malicious activity or creating false audit trails.

## Remediation

The fix introduces a sanitize_log_input() function that escapes newline characters (\n, \r) and other line-separator characters by replacing them with their escaped string representations. All user-controlled inputs are passed through this sanitization function before being included in the log message, preventing attackers from injecting fake log entries.

## Secure Code
```python
import logging
from flask import request, jsonify

logger = logging.getLogger('iot_device_manager')

def sanitize_log_input(value):
    """Remove newline and carriage return characters to prevent log injection."""
    if not isinstance(value, str):
        value = str(value)
    return value.replace('\n', '\\n').replace('\r', '\\r').replace('\x0b', '\\x0b').replace('\x0c', '\\x0c').replace('\x1e', '\\x1e').replace('\x85', '\\x85')

def register_smart_device():
    device_id = sanitize_log_input(request.json.get('device_id', 'unknown'))
    device_type = sanitize_log_input(request.json.get('device_type', 'generic'))
    firmware_ver = sanitize_log_input(request.json.get('firmware_version', 'N/A'))
    owner_email = sanitize_log_input(request.json.get('owner_email', ''))
    logger.info(f"IoT Device Registration: ID={device_id}, Type={device_type}, Firmware={firmware_ver}, Owner={owner_email}")
    if device_id and device_type:
        return jsonify({"status": "registered", "device_id": device_id})
    return jsonify({"status": "failed", "reason": "missing parameters"}), 400
```
