# Insecure Temporary File Creation via Predictable Filename and Symlink Attack

Language: Python
Severity: High
CWE: CWE-377

## Source
3

## Flow
3-4-6, 3-4-10

## Sink
6, 10

## Vulnerable Code
```python
import os
import time

def export_iot_device_logs(device_id, log_data):
    timestamp = int(time.time())
    temp_log_path = f"/tmp/iot_device_{device_id}_{timestamp}.log"
    if os.path.exists(temp_log_path):
        os.remove(temp_log_path)
    with open(temp_log_path, 'w') as log_file:
        log_file.write(f"Device ID: {device_id}\n")
        log_file.write(f"Timestamp: {timestamp}\n")
        log_file.write(log_data)
    os.system(f"gzip {temp_log_path}")
    return f"{temp_log_path}.gz"
```

## Explanation

The code creates temporary files with predictable names using timestamp and user-controlled device_id. An attacker can predict the filename and create a symlink at that location, causing the application to write sensitive log data to an attacker-controlled file or overwrite critical system files. The os.system() call also introduces command injection risk if device_id contains shell metacharacters.

## Remediation

The fix uses tempfile.mkdtemp() and tempfile.mkstemp() for secure, atomic temporary file creation that eliminates race conditions and symlink attacks. Device ID is validated with a strict regex whitelist to prevent command injection. The os.system() call is replaced with subprocess.run() using an argument list to avoid shell interpretation of special characters.

## Secure Code
```python
import os
import tempfile
import subprocess
import re
import shutil

def export_iot_device_logs(device_id, log_data):
    # Validate device_id to prevent injection attacks
    if not re.match(r'^[a-zA-Z0-9_-]+$', str(device_id)):
        raise ValueError("Invalid device_id: must contain only alphanumeric characters, hyphens, and underscores")
    
    # Create a secure temporary directory with restricted permissions
    secure_tmp_dir = tempfile.mkdtemp(prefix='iot_logs_')
    try:
        os.chmod(secure_tmp_dir, 0o700)
        
        # Use mkstemp for secure temporary file creation (atomic, no race condition)
        fd, temp_log_path = tempfile.mkstemp(
            prefix=f'iot_device_{device_id}_',
            suffix='.log',
            dir=secure_tmp_dir
        )
        
        try:
            # Write data using the file descriptor to avoid TOCTOU race
            with os.fdopen(fd, 'w') as log_file:
                log_file.write(f"Device ID: {device_id}\n")
                log_file.write(f"Timestamp: {os.path.basename(temp_log_path)}\n")
                log_file.write(log_data)
            
            # Use subprocess with argument list to avoid shell injection
            compressed_path = f"{temp_log_path}.gz"
            result = subprocess.run(
                ['gzip', temp_log_path],
                capture_output=True,
                check=True
            )
            return compressed_path
        except Exception:
            # Clean up the file descriptor if writing fails
            if os.path.exists(temp_log_path):
                os.unlink(temp_log_path)
            raise
    except Exception:
        # Clean up temporary directory on failure
        shutil.rmtree(secure_tmp_dir, ignore_errors=True)
        raise
```
