# Regla: Seguridad — Rust

Rust elimina por diseño clases enteras de vulnerabilidades (buffer overflows,
use-after-free, data races). Sin embargo, existen vectores de ataque específicos
de Rust que requieren disciplina explícita. Esta regla cubre lo que el compilador
no puede garantizar por sí solo.

---

## cargo-audit para CVEs en dependencias

```bash
# Instalar
cargo install cargo-audit

# Ejecutar auditoría
cargo audit

# Con política estricta — falla si hay cualquier vulnerabilidad
cargo audit --deny warnings
```

- `cargo audit` debe ejecutarse en CI **semanalmente** como job independiente.
- También ejecutar en el pipeline de PR si `Cargo.lock` cambia.
- Si un CVE no tiene fix disponible:
  1. Documentar en `Cargo.toml` como una excepción justificada con ticket.
  2. Evaluar si el vector de ataque aplica al uso real de la dependencia.
  3. Definir fecha límite para migrar o reemplazar la dependencia.
- NUNCA ignorar un CVE HIGH o CRITICAL sin aprobación explícita del equipo.

---

## cargo-deny para licencias y vulnerabilidades

```bash
cargo install cargo-deny
cargo deny check
```

`deny.toml` en la raíz del proyecto:

```toml
[advisories]
vulnerability = "deny"
unmaintained = "warn"
unsound = "deny"

[licenses]
# Solo licencias compatibles con el proyecto
allow = ["MIT", "Apache-2.0", "BSD-2-Clause", "BSD-3-Clause", "ISC"]
deny = ["GPL-2.0", "AGPL-3.0"]

[bans]
# Prohibir crates con historial de problemas de seguridad
deny = [
    { name = "openssl", reason = "Usar rustls o native-tls verificados" },
]
```

- `cargo-deny` en CI detecta tanto vulnerabilidades como conflictos de licencia.
- Revisar y actualizar `deny.toml` cuando se agregan dependencias nuevas.

---

## unsafe: prohibido sin justificación en ADR

- `unsafe` desactiva las garantías del compilador. Todo bloque `unsafe` es
  responsabilidad explícita del desarrollador — el compilador ya no verifica.
- **Regla**: todo uso de `unsafe` en el código del proyecto (no en dependencias)
  requiere:
  1. Un comentario inmediatamente antes del bloque explicando POR QUÉ es seguro.
  2. Un ADR documentando la decisión si el `unsafe` es en código de librería.
  3. Revisión de seguridad explícita en el PR.

```rust
// MAL — unsafe sin justificación
unsafe {
    *ptr = valor;
}

// BIEN — unsafe con safety comment obligatorio
// SAFETY: ptr apunta a memoria válida y alineada porque
// fue obtenido de Box::into_raw() en la misma función y
// no ha sido liberado ni aliasado desde entonces.
unsafe {
    *ptr = valor;
}
```

- `#![forbid(unsafe_code)]` en crates que no necesitan `unsafe` — el compilador
  rechaza cualquier bloque `unsafe` y protege contra PRs accidentales.
- Si se necesita `unsafe` en un módulo específico, aislar en un módulo dedicado
  con interfaz safe hacia afuera.

---

## Miri para detectar undefined behavior

```bash
# Instalar (requiere toolchain nightly)
rustup +nightly component add miri

# Ejecutar tests bajo Miri
cargo +nightly miri test
```

- Miri ejecuta el código en un intérprete que detecta UB, accesos inválidos
  a memoria y data races que ocurren en runtime.
- Ejecutar Miri en todo código que contiene `unsafe`.
- Miri puede ser lento — ejecutarlo como job separado en CI, no en cada PR.
- Si Miri reporta un error: es un bug real que debe corregirse, no ignorarse.

---

## No transmute sin revisión de seguridad

- `std::mem::transmute` reinterpreta los bits de un valor como otro tipo.
  Es el operador más peligroso de Rust — puede violar todas las garantías del sistema de tipos.
- **Regla**: `transmute` está prohibido sin revisión explícita de seguridad
  documentada en el PR y en el código.
- Alternativas que no requieren `transmute`:
  - Conversión de números: `as` cast o `From`/`Into`
  - Conversión de punteros: `pointer::cast()`
  - Conversión de referencias: `pointer::cast()` con `unsafe` justificado
  - Cambio de lifetime: rediseñar la API para que el compilador la acepte

---

## Secretos: no en const/static

```rust
// MAL — la API key queda en el binario compilado y en el historial de git
const API_KEY: &str = "sk-prod-abc123xyz789";

// MAL — static mutable tampoco es seguro
static mut TOKEN: String = String::new();

// BIEN — leer de variable de entorno en runtime
fn cargar_config() -> Result<Config> {
    Ok(Config {
        api_key: env::var("API_KEY")
            .context("Variable API_KEY no definida")?,
    })
}
```

- Las constantes en Rust se embeben en el binario. Un secreto en `const` puede
  extraerse del ejecutable con herramientas básicas de análisis binario.
- Usar variables de entorno para secretos, inyectadas por el sistema de deployment.
- En desarrollo local: archivo `.env` (en `.gitignore`) con `dotenvy` crate.

---

## Validar inputs en boundaries del sistema

```rust
use garde::Validate;

#[derive(Validate)]
pub struct NuevaFactura {
    #[garde(length(min = 1, max = 255))]
    pub descripcion: String,

    #[garde(range(min = 0.01, max = 1_000_000.0))]
    pub total: f64,

    #[garde(email)]
    pub cliente_email: String,
}

pub async fn crear_factura(
    State(repo): State<Arc<FacturaRepo>>,
    Json(input): Json<NuevaFactura>,
) -> Result<Json<Factura>, AppError> {
    input.validate(&())?; // Retorna error si algún campo es inválido
    let factura = repo.crear(input).await?;
    Ok(Json(factura))
}
```

- Validar toda entrada externa (HTTP, archivos, CLI) en el boundary de entrada.
- No asumir que los datos de la base de datos son válidos si pueden haber sido
  escritos por otros sistemas.
- Usar el crate `garde` o `validator` para validación declarativa.

---

## Pin para futures auto-referenciales

```rust
use std::pin::Pin;
use futures::Future;

// Cuando un future contiene referencias a sí mismo, Pin garantiza
// que no se moverá en memoria mientras está pendiente.
type BoxFuture<'a, T> = Pin<Box<dyn Future<Output = T> + Send + 'a>>;

// En traits async con objetos (trait objects de futures)
trait Procesador: Send {
    fn procesar<'a>(&'a self, item: &'a Item) -> BoxFuture<'a, Result<()>>;
}
```

- Usar `Pin<Box<dyn Future>>` para almacenar futures en structs o como parámetros.
- No implementar `Unpin` manualmente en futures a menos que se pueda demostrar
  que son seguros de mover.
- El compilador detectará la mayoría de los errores relacionados con Pin —
  si hay que combatir al compilador para que acepte el código, revisar el diseño.

---

## Checklist de seguridad antes de hacer merge

- [ ] `cargo audit` pasa sin vulnerabilidades HIGH o CRITICAL
- [ ] `cargo deny check` pasa (licencias y vulnerabilidades)
- [ ] Todo bloque `unsafe` tiene safety comment explicando por qué es seguro
- [ ] Sin `transmute` sin revisión de seguridad documentada
- [ ] Sin secretos en `const`, `static` o en código fuente
- [ ] Inputs externos validados con esquemas (garde/validator)
- [ ] `#![forbid(unsafe_code)]` en crates que no lo necesitan
- [ ] Miri ejecutado en todo código nuevo con `unsafe`
