# SQL Injection via f-string in psycopg2.execute()

Language: Python
Severity: Critical
CWE: CWE-89

## Source
14

## Flow
14-4-6

## Sink
6

## Vulnerable Code
```python
import psycopg2

def fetch_iot_device_telemetry(conn, device_mac, metric_type):
    cur = conn.cursor()
    query = f"SELECT timestamp, value, unit FROM sensor_data WHERE device_id = '{device_mac}' AND metric = '{metric_type}' ORDER BY timestamp DESC LIMIT 100"
    cur.execute(query)
    telemetry_records = cur.fetchall()
    cur.close()
    return telemetry_records

def get_device_metrics(mac_address, sensor_metric):
    db_conn = psycopg2.connect("dbname=iot_platform user=admin password=secret")
    results = fetch_iot_device_telemetry(db_conn, mac_address, sensor_metric)
    db_conn.close()
    return results
```

## Explanation

The function accepts user-controlled parameters 'mac_address' and 'sensor_metric' which are directly interpolated into an SQL query using f-strings without any sanitization or parameterization. This allows an attacker to inject malicious SQL code that will be executed by the psycopg2 cursor.execute() method.

## Remediation

The fix replaces the f-string interpolation with psycopg2's parameterized query mechanism, using %s placeholders and passing the parameters as a tuple to cur.execute(). This ensures that user-supplied values are properly escaped and treated as data rather than executable SQL code, completely preventing SQL injection attacks.

## Secure Code
```python
import psycopg2

def fetch_iot_device_telemetry(conn, device_mac, metric_type):
    cur = conn.cursor()
    query = "SELECT timestamp, value, unit FROM sensor_data WHERE device_id = %s AND metric = %s ORDER BY timestamp DESC LIMIT 100"
    cur.execute(query, (device_mac, metric_type))
    telemetry_records = cur.fetchall()
    cur.close()
    return telemetry_records

def get_device_metrics(mac_address, sensor_metric):
    db_conn = psycopg2.connect("dbname=iot_platform user=admin password=secret")
    results = fetch_iot_device_telemetry(db_conn, mac_address, sensor_metric)
    db_conn.close()
    return results
```
