# MacLockdown — Registro de Arquitectura y Cambios

**Versión estable:** 0.7
**Fecha:** 2026-03-15
**Estado:** ✅ CTI integrado · PDF enriquecido · Attack Surface con TTPs · Compare Baseline · Beta pública disponible

---

## Resumen ejecutivo

MacLockdown es un motor de auditoría de seguridad para macOS con UI nativa. Evalúa hasta 39 controles en 5 dominios, aplica fixes con privilegios de administrador, visualiza la superficie de ataque por vector de riesgo, compara contra baselines CIS Level 1 y Level 2, y genera reportes PDF profesionales con inteligencia de amenazas (CTI) que mapea cada control a técnicas MITRE ATT&CK y grupos APT conocidos.

La arquitectura es data-driven — las definiciones de controles viven en `checks.json` (schema v1.2). Agregar un control o un TTP no requiere tocar Swift.

---

## Métricas del producto (v0.7)

| Métrica | Valor |
|---|---|
| Controles auditados | 39 |
| Dominios | System (11) · Disk (4) · Firewall (3) · Network (14) · Privacy (7) |
| Perfiles | Basic (9) · Hardened (36) · Paranoid (39) |
| Tipos de acción | Auto (27) · Manual (8) · Info (4) |
| Vectores de ataque | 6 |
| TTPs mapeados | 33 (de 39 checks) |
| Referencias a grupos APT | 73 |
| Tácticas MITRE cubiertas | 14 |
| Pool de peso total | 173 pts |
| Archivos Swift | 14 |
| Líneas totales | ~5.600 |
| Tiempo de inicio | ~3.5 seg (cold start) |
| Tiempo de scan | ~5–8 seg (paralelo por dominio) |
| Plataforma validada | macOS Tahoe 26.3 · Apple Silicon M5 |

---

## Mapa de archivos

| Archivo | Líneas | Rol | Cuándo tocarlo |
|---|---|---|---|
| `checks.json` | 1.931 | Catálogo v1.2 — controles + TTPs + APT groups | Agregar controles, TTPs, o grupos APT |
| `ContentView.swift` | 1.740 | UI completa | Cambiar presentación visual |
| `ReportEngine.swift` | 618 | Markdown + PDF con CTI | Cambiar formato de reportes |
| `SecurityViewModel.swift` | 290 | Orquestador | Cambiar flujo de negocio |
| `ScanEngine.swift` | 215 | Evaluación de checks | Calibrar lógica de interpretación |
| `AttackSurface.swift` | 208 | Attack Surface — modelo y engine | Agregar vectores o lógica de exposición |
| `CheckLoader.swift` | 131 | Decodifica JSON → `[SecurityCheck]` | Si cambia el schema |
| `SecurityCheck.swift` | 114 | Modelo de datos — schema v1.2 | Si se agregan campos |
| `PersistenceEngine.swift` | 114 | UserDefaults | Cambiar qué se persiste |
| `FixEngine.swift` | 60 | Fixes + URLs System Settings | Agregar URLs |
| `ShellEngine.swift` | 55 | Ejecuta bash y AppleScript | — |
| `VerboseLog.swift` | 54 | Modelos de log verbose | — |
| `ScoreEngine.swift` | 48 | Score ponderado | Cambiar algoritmo |
| `MacLockdownApp.swift` | 17 | Entry point + ventana | — |

---

## 1. Schema v1.2 — `checks.json`

### Nuevo campo: `mitreTTPs`

Cada check puede tener un array de TTPs. Formato:

```json
"mitreTTPs": [
  {
    "ttpId": "T1021.004",
    "ttpName": "Remote Services: SSH",
    "tactic": "Lateral Movement",
    "description": "SSH tunneling usado para C2 y lateral movement...",
    "groups": [
      {
        "name": "Lazarus Group",
        "origin": "North Korea (RGB)",
        "aka": "SSH tunneling for C2 and data exfiltration in financial heists"
      },
      {
        "name": "APT29 (Cozy Bear)",
        "origin": "Russia (SVR)",
        "aka": "SolarWinds — SSH used for lateral movement"
      }
    ]
  }
]
```

### Checks sin TTPs (6/39)

`secure_token`, `internet_sharing`, `wake_on_network`, `media_sharing`, `lockdown_mode`, `screensaver_timeout` — checks informativos o sin técnica MITRE directamente aplicable.

### Estadísticas del catálogo CTI

| Métrica | Valor |
|---|---|
| Checks con TTPs | 33/39 |
| Total de TTPs | 33 |
| Referencias a grupos APT | 73 |
| Tácticas MITRE cubiertas | 14 |

### Tácticas cubiertas

Collection · Defense Evasion · Discovery · Exfiltration · Execution · Initial Access · Lateral Movement · Persistence · Privilege Escalation · Reconnaissance · Credential Access

### Grupos APT más referenciados

| Grupo | Origen | Checks que explotan |
|---|---|---|
| APT28 (Fancy Bear) | Russia (GRU) | 6 |
| APT41 | China (PLA) | 4 |
| APT29 (Cozy Bear) | Russia (SVR) | 3 |
| APT32 (OceanLotus) | Vietnam | 3 |
| APT10 (Stone Panda) | China (MSS) | 3 |
| NSO Group (Pegasus) | Israel (commercial) | 3 |
| OSX.Shlayer | Cybercriminal | 3 |
| Silver Sparrow | Unknown | 2 |

---

## 2. Modelo — `SecurityCheck.swift` (schema v1.2)

### Nuevos structs

```swift
struct AptGroup: Codable {
    let name: String      // "APT29 (Cozy Bear)"
    let origin: String    // "Russia (SVR)"
    let aka: String       // descripción breve
}

struct MitreTTP: Codable {
    let ttpId: String      // "T1021.004"
    let ttpName: String    // "Remote Services: SSH"
    let tactic: String     // "Lateral Movement"
    let description: String
    let groups: [AptGroup]
    var url: String        // URL MITRE ATT&CK computada
}
```

### Campo agregado al modelo principal

```swift
var mitreTTPs: [MitreTTP]  // schema v1.2 — array vacío si no aplica
```

---

## 3. Arquitectura de engines

```
SecurityViewModel  (@MainActor · orquestador · 290 líneas)
├── CheckLoader          lee checks.json → [SecurityCheck] (schema v1.2)
├── ScanEngine           evalúa cada check por ID
│     └── ShellEngine    ejecuta bash / AppleScript
├── ScoreEngine          score ponderado (función pura)
├── FixEngine            aplica fixes + abre System Settings
├── ReportEngine         genera Markdown y PDF con CTI
├── PersistenceEngine    UserDefaults
└── AttackSurfaceEngine  analiza vectores de ataque
```

---

## 4. ReportEngine — PDF con CTI

### Dos formatos de export

| Formato | Método | Mecanismo | Salida |
|---|---|---|---|
| Markdown | `exportMarkdown()` | File write | `.md` en Desktop |
| PDF | `exportPDF()` | `WKWebView.createPDF()` | `.pdf` en Desktop |

### Flujo PDF

```
exportPDF()
  1. buildHTML() → HTML string con estilos propios
  2. WKWebView offscreen (794×1123 px, A4)
  3. loadHTMLString()
  4. asyncAfter(1.5s) → render completo
  5. createPDF(configuration:) → Data
  6. write(to: Desktop/MacLockdown_Report_DATE.pdf)
  7. activateFileViewerSelecting() → Finder muestra el archivo
```

Sin `NSPrintOperation` — sin diálogos, sin bloqueo del main thread.

### Estructura del PDF

```
[Header oscuro]
  Score circle SVG · MacLockdown · Host/macOS/fecha · Stats grid

[Risk banner]       — solo si hay accepted risks

[❌ FAILED]
  Por cada check:
  ├── Nombre + badge FAIL
  ├── Level · weight · profiles · framework badges
  ├── What it does
  ├── Why it matters
  ├── How to revert
  └── [⚠ THREAT INTELLIGENCE]  — si tiene TTPs
      ├── TTP ID badge + nombre + táctica
      ├── Descripción de por qué aplica a este check
      └── Lista de grupos APT: nombre · origen · operaciones conocidas

[⚠ ACCEPTED RISK]   — checks ignorados con fallo real
[⏭ IGNORED]         — checks ignorados que pasan
[✅ PASSED]

[⚠ THREAT ACTORS]   — solo si hay fallos con TTPs
  Grid de todos los grupos APT que pueden explotar los gaps
  detectados en este sistema específico
  Ordenados por cantidad de checks que explotan
```

---

## 5. UI — TTPs en la interfaz

### Attack Surface — checks expandibles

Cuando el usuario expande una card de vector, cada check muestra:

```
● Remote Login    [T1021.004]    FAIL
● Screen Sharing  [T1021.005]    FAIL
● File Sharing    [T1021.002]    PASS
```

El badge `T1021.004` es naranja, con tooltip que muestra el nombre completo de la técnica y la táctica al hacer hover.

### Check row — drawer expandible

La sección de drawer incluye ahora:

```
FRAMEWORK REFERENCES    CIS 3.1  NIST SC-7

MITRE ATT&CK
  T1021.004  Remote Services: SSH  ·  Lateral Movement  ·  3 known groups
```

El detalle completo (quiénes son los grupos, operaciones conocidas) vive exclusivamente en el PDF.

---

## 6. Compare Baseline

Panel modal accesible desde el header. Botón azul `COMPARE`.

### Anillos concéntricos

```
Exterior (púrpura, 190pt) → CIS Level 2 — 22 controles de alta seguridad
Medio    (azul, 156pt)    → CIS Level 1 — 10 controles básicos para todos
Interior (verde, 122pt)   → Tu score actual — el más prominente
Centro                    → número del score actual
```

Animaciones escalonadas: interior (0s) → medio (0.1s) → exterior (0.2s).

### Baselines definidos

**CIS Level 1** (10 checks): `firewall`, `filevault`, `sip`, `gatekeeper`, `auto_updates`, `screen_lock`, `auto_login`, `guest_user`, `stealth_mode`, `remote_login`

**CIS Level 2** (22 checks): Level 1 + `file_sharing`, `printer_sharing`, `remote_apple_events`, `screen_sharing`, `airdrop`, `internet_sharing`, `wake_on_network`, `secure_token`, `ad_tracking`, `siri_analytics`, `audit_logging`, `sudo_timeout`

### Secciones de gaps

Debajo de los anillos, dos secciones de gaps con los checks que fallan en cada baseline — con referencia CIS y peso.

---

## 7. UI — Header y navegación

### Componentes del header

```
[Score circle] [MACLOCKDOWN / label / last scan]
[TrendProfileView]                              [ATTACK SURFACE] [COMPARE] [EXPORT ▾] [VERBOSE] [SCAN]
```

### TrendProfileView

Bloque unificado: perfiles arriba + sparkline abajo.

```
[BASIC | HARDENED | PARANOID]  +6↑
[▄▄▄▅▅▆▆▇▇█]                  ← barras del historial con label "TREND"
```

- Perfiles con `.fixedSize()` — no hacen wrap
- Trend badge inline con los perfiles
- Barras con tooltip de score + fecha por barra

### Orden de botones

`ATTACK SURFACE → COMPARE → EXPORT (menú MD/PDF) → VERBOSE → SCAN`

### Botón EXPORT

`Menu` nativo de macOS con dos opciones:
- `Export Markdown` → `vm.exportReport()`
- `Export PDF` → `vm.exportPDF()`

---

## 8. Attack Surface panel

### Layout de cards

Dos `VStack` paralelas en vez de `LazyVGrid` — evita desalineación al expandir cards. La columna con menos cards usa `.background(Color(hex: "#0a0d12"))` para que el espacio vacío sea invisible.

### ExposureLevel

```
none → low → medium → high → critical
```

Cada vector tiene `exposureReason` — string legible que explica el nivel. Los vectores se ordenan de mayor a menor exposición.

### Checks vacíos por perfil

`analyze()` usa `compactMap` — vectores sin checks del perfil activo se omiten. En BASIC solo aparecen los vectores con checks relevantes.

---

## 9. Persistencia

| Clave UserDefaults | Tipo | Contenido |
|---|---|---|
| `ml.ignoredIDs` | `[String]` | IDs marcados como intencionales |
| `ml.lastScanDate` | `Date` | Fecha del último scan |
| `ml.scanHistory` | `Data` (JSON) | Array de `ScanRecord`, máx 30 |
| `ml.activeProfile` | `String` | Perfil activo al cerrar |

---

## 10. Calibración en hardware real

**Plataforma:** MacBook Pro M5 14", macOS Tahoe 26.3 (Build 25D125)

### Score de referencia

| Perfil | Score | Fallos típicos |
|---|---|---|
| Basic | ~90/100 | Screensaver timeout |
| Hardened | ~84/100 | Screensaver · Bonjour · Diagnostics · Safari · Stealth · Audit · Sudo |
| Paranoid | ~83/100 | + TCP timestamps · Captive portal |

### Falsos positivos corregidos (M5/Tahoe)

`secure_token` · `auto_login` · `mrt` · `xprotect` · `rapid_security_response` · `remote_management` · `lockdown_mode` · `auto_login` (duplicado)

---

## 11. Concurrencia Swift 6

**Scan paralelo por dominio** — `withTaskGroup` en `SecurityViewModel.runAudit()`. Los 5 dominios corren concurrentemente. Tiempo: de ~22 seg secuencial a ~5–8 seg paralelo.

**Fixes aplicados:**
- `Task.detached(priority: .utility)` — evita priority inversion
- Snapshot `checksSnapshot` antes del `TaskGroup` — evita acceso a `@MainActor` desde contexto concurrente
- `nonisolated` en `resolvedCommands()`, `belongs(to:)`, `affectsScore` — accesibles desde `TaskGroup`

---

## 13. Beta pública — distribución Basic

### Objetivo

Distribución pública de MacLockdown en modo Basic (9 controles) con exportación PDF. Sin precio, sin notarización, sin funcionalidades de Hardened o Paranoid. El objetivo es validar conversión, recoger feedback técnico y construir la waitlist.

### Flag de control

Un único booleano en `SecurityViewModel.swift` controla todo el comportamiento beta:

```swift
static let isBeta = true
```

Para lanzar la versión completa: cambiar a `false`. Ningún otro archivo necesita modificación.

### Cambios por archivo

| Archivo | Cambio | Detalle |
|---|---|---|
| `SecurityViewModel.swift` | `isBeta = true` · perfil forzado a `.basic` | `init()` ignora `PersistenceEngine.loadProfile()` · `setProfile()` es no-op |
| `ContentView.swift` | UI simplificada | Badge `BETA` ámbar en header · `TrendProfileView` oculto · botones `ATTACK SURFACE` y `COMPARE` ocultos |
| `ReportEngine.swift` | Reportes marcados como beta | Filename: `MacLockdown_Report_Beta_DATE` · Badge `BETA · BASIC` en header PDF · Título Markdown con sufijo `— Beta · Basic Profile` |
| `MacLockdownApp.swift` | Título de ventana | `"MacLockdown Beta"` en lugar de `"MacLockdown"` |

### Perfil beta

| Parámetro | Valor |
|---|---|
| Perfil activo | Basic (forzado) |
| Controles auditados | 9 |
| Attack Surface | No disponible |
| Compare Baseline | No disponible |
| Selector de perfil | Oculto |
| Export PDF | ✅ Disponible |
| Export Markdown | ✅ Disponible |
| Verbose log | ✅ Disponible |

### Distribución

- Formato: `.zip` con `MacLockdown.app` dentro
- Nombre del archivo: `MacLockdown-beta.zip`
- Notarización: no — usuario abre con click derecho → Abrir
- Requisitos: macOS Ventura 13.0+, Apple Silicon e Intel
- Instalación: arrastrar a Applications — no requiere installer

### Header del PDF en beta

```
MACLOCKDOWN  [BETA · BASIC]
Good protection
MacBook Pro de Nombre · macOS Tahoe 26.3 · 2026-03-15
```

### Reactivar versión completa

```swift
// SecurityViewModel.swift — una sola línea
static let isBeta = false
```

Esto restaura automáticamente: selector de perfil, botones Attack Surface y Compare, título de ventana, filenames de reporte sin sufijo Beta, y lectura del perfil guardado en UserDefaults.

---

## 14. Pendiente

### Backlog de producto
- **Nivel Standard** entre Basic y Hardened — solo requiere editar `profiles[]` en JSON (decisión pendiente)
- **Notificaciones de score** — alerta cuando el score baja entre sesiones
- **ARCHITECTURE.md** — actualizar en cada sprint

### Optimizaciones técnicas
- **`domainOrder` lazy** — cachear con `lazy static` para reducir inicio a ~2 seg
- **Scan en paralelo** — ya implementado; medir tiempos reales post-deploy con 60+ controles
