Parte IV — Copa, suelo y cuidado
11 · Cuidar el árbol
El baobab viejo tiene un hueco en el tronco, y el hueco también es parte del árbol. Este módulo lista los huecos que encontró la revisión de octubre de 2026 — los que se cerraron y los que quedan como trabajo de la comunidad.
Corregidos en la 3.3
| # | Problema | Dónde | Cómo se cerró | Prueba |
|---|---|---|---|---|
| 1 | Inbox ActivityPub siempre montada, sin firma, con upsert en el uuid elegido por quien envía: cualquiera sobrescribía el saber de un OXALÁ, de la mucua o de la galaxia, y el commit se propagaba en el sync | api/v3/activitypub.py, federacao/ingestao.py | ruta solo con BBX_FED_AP=on; HTTP Signature de una mucua conocida (Digest + Date); la ingestión no toca saberes locales, no acepta identidad ni uuid mal formado | test_federacao_inbox_segura.py |
| 2 | Token EdDSA de otra mucua: cualquier mucua conocida emitía un token con el sub de un OXALÁ local y mucua = local, y obtenía la administración de la instancia; una copia falsificada del saber de la mucua local permitía emitir tokens "locales" | services/auth.py | emisor local solo con la clave del disco; una mucua de fuera solo responde por sus propias mocambolas; mucua y username recalculados; IdP externo siempre USER | test_token_federado_recalcula_papel.py |
| 3 | Descarga de anexos, miniaturas y portada de colección sin control de lectura: acl, privado, borrador y galaxia restringida salían para anónimos | services/saber_service.py, api/v3/saberes.py, colecoes.py | content_path, thumbnail_path, colecao_mosaico_path reciben actor y pasan por can(READ) | test_vazamentos_de_leitura.py |
| 4 | XSS: [x](javascript:…) en el markdown y URLs de campos del saber se volvían enlaces ejecutables | front lib/util/markdown.ts y páginas | hrefSeguro | — |
| 5 | SVG servido inline por la API en el mismo origen del sitio ejecutaba scripts | api/v3/saberes.py | CSP sandbox + nosniff | — |
| 6 | Sin límite de intentos de contraseña (/auth/login, /auth/token, /xmpp/extauth) | services/auth.py | freno por cuenta (10 errores en 15 min) | test_freio_login.py |
| 7 | git clone <url> con url venida de la API: --upload-pack=… ejecutaría un comando | infra/sync/*.py | -- y rechazo de urls que empiezan con - | — |
| 8 | extauth del XMPP alcanzable desde internet | nginx | allow 127.0.0.1 | — |
Huecos que quedan (para que la comunidad decida)
- El
password_hashviaja en el git. Toda galaxia clonada lleva los hashes de todas las cuentas. PBKDF2 con 200 mil rondas retrasa, pero no impide un ataque offline. Camino: sacar la contraseña delsaber.bbx(como las claves privadas) o cambiar a Argon2/scrypt, más caros. - Token de 7 días con el rol sellado. Bajar de rol a alguien solo vale en el próximo login. Los grupos ya se resuelven en cada petición — el rol puede seguir el mismo camino, o el token puede durar menos con renovación.
- Token en la query string (
?access_token=) para<img>/<video>: queda en el log del nginx y en el historial. Camino: token corto solo para medios, o cookieHttpOnlyleída por la API. - Token en el
localStorage+ cookie sinHttpOnly. Cualquier XSS roba la sesión. ElhrefSegurocierra el caso conocido; una CSP en el front cerraría la clase entera. BBX_CADASTRO_ABERTO=onpor defecto en el paquete: en una mucua expuesta a internet, cualquiera crea una cuenta y sube archivos de hasta 4 GB, que se sincronizan hacia la red.- La API escucha en
0.0.0.0:8000(necesario para la red local y para Tor), con/docsabierto. ConsiderarBBX_DOCS=offen producción. - Federación AP incompleta: las entregas ya salen firmadas, pero no hay registro de seguidores (
seguidores_inbox), y la pubkey de una mucua puede venir de cualquier galaxia instalada. Antes de activarBBX_FED_AP, definir en qué galaxias se confía. SaberServicecon 4000 líneas. Dividirlo por tema (lectura, escritura, colecciones, eventos, estructura) facilita la revisión de seguridad.- Front con ~55 errores de tipo (
npm run check). permissions.pode_administrar_instanciadevuelveTruecuando no consigue leer la identidad local ("comportamiento antiguo"). En producción eso no pasa, pero fallar cerrado sería más seguro.
Ejercicios de fin de curso
- Hueco 2: haz que el rol se recalcule en
deps._moc_from_tokenen cada petición (como los grupos), con caché corta. Escribe la prueba antes. - Hueco 6: añade
BBX_DOCS=offenapi/v3/app.pyy en elbackend.envdel paquete. - Hueco 4: escribe una Content-Security-Policy para SvelteKit (
kit.cspensvelte.config.js) y descubre qué se rompe. - Nueva nature: crea la nature
cantiga(letra, intérprete, territorio, audio) solo por la API, sin tocar el código. ¿Qué faltó? - Ruta de sneakernet real: arma, con dos máquinas y un pendrive, un pedido que atraviese tres mucuas.
Glosario
| Término | En el código | Quiere decir |
|---|---|---|
| saber | core/saber.py | cualquier cosa guardada; la unidad |
| nature | natures/ | el tipo de un saber |
| mucua | infra/mucua_local.py | una instancia (el fruto del baobab) |
| galaxia (galáxia) | SaberRepository | un acervo = un repositorio git |
| mocambola | nature mocambola | una persona |
| territorio (território) | nature territorio | el suelo de una mucua |
| Sankofa | infra/sankofa.py | la fachada del git/annex (volver y buscar) |
| link | services/links.py | un remote git de una galaxia |
| ruta (rota) | nature rota | camino físico de sneakernet |
| pedido | nature pedido | "quiero una copia de este archivo" |
| lente / aplicación | applications/ | una forma de ver los saberes |
| OXALÁ, PAJÉ, USER | constants.ROLE_* | roles (galaxia, mucua, uno mismo) |
| gestor local | mucua_local.gestores_locais | coadministrador invitado por esta máquina |
| procedencia (procedência) | data.enriquecimento | lo que la máquina hizo con un anexo |
| faro (farol) | infra/farol_lan.py | el anuncio en la red local |
Cierre
Un baobab no es de quien lo plantó: es de quien se sienta debajo de él. El código también. Léanlo juntos, discrepen de los comentarios, escriban pruebas para las historias de su comunidad.
