Skip to content

Commit d600d87

Browse files
committed
docs(security): marcar N-1, N-2 y N-3 como resueltos en SECURITY_AUDIT.md
Actualiza tabla de resultados (ALTO pendientes 3→0) y añade referencia al commit de resolución en cada hallazgo de la segunda auditoría.
1 parent 752900b commit d600d87

1 file changed

Lines changed: 62 additions & 6 deletions

File tree

SECURITY_AUDIT.md

Lines changed: 62 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,68 @@
77

88
## Resultado
99

10-
| Severidad | Encontrados | Resueltos | Aceptados |
11-
|-----------|-------------|-----------|-----------|
12-
| CRÍTICO | 8 | 8 | 0 |
13-
| ALTO | 14 | 12 | 2 |
14-
| MEDIO | 8 | 8 | 0 |
15-
| BAJO | 7 | 6 | 1 |
10+
| Severidad | Encontrados | Resueltos | Aceptados | Pendientes |
11+
|-----------|-------------|-----------|-----------|------------|
12+
| CRÍTICO | 8 | 8 | 0 | 0 |
13+
| ALTO | 17 | 15 | 2 | 0 |
14+
| MEDIO | 8 | 8 | 0 | 0 |
15+
| BAJO | 7 | 6 | 1 | 0 |
16+
17+
---
18+
19+
## Hallazgos resueltos — Segunda auditoría 2026-07-29
20+
21+
### N-1 · `HelmMusicService` exportado sin permiso de bind ✅ resuelto en 9f947a3
22+
**Archivos:** `app/src/main/AndroidManifest.xml:84-91` · `audio/src/main/kotlin/dev/helm/audio/HelmMusicService.kt:41-45`
23+
**Severidad:** Alta
24+
25+
`HelmMusicService` tiene `android:exported="true"` sin `android:permission` en el `<service>`. El guard en `onGetSession` compara `controllerInfo.packageName == packageName`, pero ese valor lo suministra el caller vía `connectionHints` — no está verificado por el sistema. Cualquier app puede hacer bind y controlar la reproducción o inyectar URIs al queue de ExoPlayer.
26+
27+
**Fix:**
28+
```xml
29+
<service
30+
android:name=".HelmMusicService"
31+
android:permission="android.permission.MEDIA_CONTENT_CONTROL"
32+
android:exported="true">
33+
```
34+
En `onGetSession`: confiar solo en `controllerInfo.isTrusted`, eliminar la comparación por `packageName`.
35+
36+
---
37+
38+
### N-2 · OTA — URL de descarga sin validación de host/scheme ✅ resuelto en 36e5b05
39+
**Archivo:** `ota/src/main/kotlin/dev/helm/ota/OtaRepository.kt:45-56`
40+
**Severidad:** Alta
41+
42+
`browser_download_url` de la respuesta JSON de GitHub se pasa verbatim a `URL(apkUrl).openConnection()` sin verificar scheme ni host. `HttpURLConnection` sigue redirects por defecto. Con cuenta de GitHub comprometida o DNS poisoning se puede redirigir la descarga a un servidor arbitrario.
43+
44+
**Fix:**
45+
```kotlin
46+
val parsed = URL(apkUrl)
47+
require(parsed.protocol == "https") { "Scheme inválido" }
48+
require(
49+
parsed.host.endsWith(".github.com") ||
50+
parsed.host.endsWith(".githubusercontent.com")
51+
) { "Host no permitido" }
52+
conn.instanceFollowRedirects = false
53+
```
54+
55+
---
56+
57+
### N-3 · OTA — Comparación de firma APK con lógica `any/any` ✅ resuelto en 752900b
58+
**Archivo:** `ota/src/main/kotlin/dev/helm/ota/OtaRepository.kt:102-104`
59+
**Severidad:** Alta
60+
61+
La verificación usa `apkCerts.any { apk -> installedCerts.any { ... } }` — pasa si el APK descargado comparte **al menos un** certificado con el instalado. Si se rota la clave de firma y la anterior queda comprometida, un APK firmado con esa clave vieja seguiría pasando el check. Agravado por N-2.
62+
63+
**Fix:**
64+
```kotlin
65+
// Igualdad estricta de conjuntos
66+
require(
67+
apkCerts.map { it.toCharsString() }.toSet() ==
68+
installedCerts.map { it.toCharsString() }.toSet()
69+
)
70+
```
71+
Opción más robusta: comparar contra un fingerprint SHA-256 hardcodeado del cert de release, eliminando dependencia del runtime.
1672

1773
---
1874

0 commit comments

Comments
 (0)