Skip to content

Security: alcancia-app/alcancia

SECURITY.md

Seguridad de Alcancía

Alcancía guarda tus finanzas en tu computador, así que un error de seguridad puede importar mucho. Si encontraste uno, gracias por avisarnos en privado. (English below.)

Cómo avisar (en privado)

  1. Entra a Reportar una vulnerabilidad (pestaña Security del repo → Report a vulnerability). Solo lo ve quien mantiene el proyecto.
  2. Cuenta qué pasa, cómo repetirlo y qué podría hacer alguien con eso. Si necesitas un archivo de ejemplo, arma uno ficticio: nunca mandes una cartola real, llaves de un servicio ni datos tuyos.
  3. No abras un issue público ni lo cuentes en Discussions hasta que esté arreglado.

Qué puedes esperar

  • Te respondemos en 5 días hábiles como máximo (es un proyecto chico, mantenido por personas).
  • En 30 días te contamos qué vamos a hacer y, si se puede, ya está arreglado. Lo grave va primero.
  • Publicamos el aviso de seguridad cuando haya versión arreglada, con tu nombre si quieres. Si después de 90 días no hay arreglo, conversamos cómo hacerlo público.
  • Si investigas de buena fe, sin tocar datos de otras personas ni dañar nada, no vamos a tomar ninguna acción en tu contra.

Qué cubre

Todo lo de este repo y lo que sale de él: la app de Mac (y las de Windows y Linux cuando existan), la app del celular (movil/, iPhone y Android), los scripts de bin/, la interfaz de ui/, la copia cifrada para el celular, los conectores, el servidor MCP, las actualizaciones, la página publicada y los workflows de GitHub. También el catálogo de recetas.

Nos interesa especialmente:

  • Que una cartola, una receta, una traducción, la descripción de un movimiento o la respuesta de una IA logre ejecutar código, leer archivos o usar el puente con la app.
  • Que una llave de un conector salga del Llavero hacia otro lugar que no sea el dominio oficial del servicio.
  • Que alguien pueda hacer llegar una actualización falsa de Alcancía.
  • Que la IA de la persona vea más de lo que dice el README (nunca números de cuenta, RUT ni claves).
  • Que la copia para el celular se pueda abrir sin la frase, cambiar sin que se note, o lleve algo que no debería (llaves, tokens, rutas del computador): el formato está en el README.
  • Que un pull request pueda robar secretos o permisos de los workflows.

No cubre: lo que exige tener ya el control de tu computador o de tu cuenta de usuario, problemas de la IA que tú elegiste (Claude, ChatGPT/Codex o Gemini) o de los servicios con los que conectas, ni ataques de ingeniería social.

Cómo nos cuidamos (resumen)

  • Sin servidores propios ni telemetría; tus datos viven en ~/Alcancia, solo para tu usuario.
  • Nunca pedimos la clave del banco. Las llaves de los conectores van al Llavero, viajan por la entrada estándar y solo a los dominios declarados de cada servicio, sin seguir redirecciones.
  • Recetas = solo datos, validadas. Todo lo que viene de afuera se escapa antes de mostrarse y la interfaz tiene una política de contenido (CSP) que no deja correr código que no venga con la app.
  • La IA corre en una carpeta temporal, sin herramientas ni conexiones extra, y lo que propone se valida y la persona lo confirma.
  • Al celular los datos llegan cifrados (PBKDF2-SHA256 de 600.000 vueltas + AES-256-GCM, IV nuevo en cada escritura) en la nube de la persona; la frase no se guarda en ninguna parte (el Mac guarda la llave derivada en su Llavero, el celular solo si la persona lo pide, en el Llavero o el Keystore). La app del celular no tiene permiso de internet, ni respaldo automático en Android, ni excepciones de ATS en iOS.
  • La app de Mac es la pieza «host-tauri» de Pieza (escritorio/src/pieza, Rust + Tauri): el puente solo atiende la ventana propia con su página en ui/ y la ventana nunca navega fuera; lo que pide la interfaz se valida contra acciones.json antes de correr nada: lo que va como argumento es solo un valor de una lista cerrada o algo ya revisado (el id de un conector, una receta, el país), lo demás va en un archivo de entrada en una carpeta temporal 0700 y el script lo vuelve a revisar, y una ruta absoluta solo vale si es la cartola que la persona eligió en el diálogo del sistema. Cada script tiene límite de tiempo (se corta también lo que lanzó, y al salir no queda nada corriendo) y corre sin variables ALCANCIA_* de afuera. Las llaves van al llavero del sistema (Secretos, solo en ese Mac) y llegan al script solo por la entrada estándar.
  • Todo cambio entra por pull request revisado. Las actions de GitHub están fijadas por SHA, con permisos mínimos y sin pull_request_target.
  • Actualizaciones (pieza «actualizaciones»): solo de https://github.com/alcancia-app/alcancia/releases/download/…, la redirección de GitHub solo por https y hoy se verifica la huella SHA-256 publicada junto al Release; nunca con un guardado o un script corriendo. La firma de las actualizaciones (para que ni siquiera alguien con acceso a la cuenta de GitHub pueda publicar una versión falsa) está diseñada y pendiente: ver abajo.

Firma de las actualizaciones (diseño, pendiente)

Estado hoy: todavía no hay Releases publicados. Cuando los haya, la app baja Alcancia-macOS.zip y su .sha256 del mismo Release: eso detecta una descarga rota, pero no una versión falsa subida por alguien que tome la cuenta de GitHub (subiría las dos cosas). La app va firmada ad hoc y el actualizador le quita la cuarentena, así que macOS tampoco la revisa.

Lo que se propone, antes del primer Release:

  1. Una llave Ed25519 que se crea una sola vez en el Mac de quien mantiene el proyecto (CryptoKit Curve25519.Signing), con la privada en el Llavero (no sincronizada, ojalá con confirmación) y un respaldo fuera de línea. Nunca en GitHub ni en un secreto de Actions: si la cuenta cae, la llave no cae con ella.
  2. La pública se escribe en la app (en la pieza «actualizaciones» de Pieza, escritorio/src/pieza/actualizaciones.rs, que se configura con una constante en base64 de Alcancía: el cambio va primero a Pieza). Se puede tener una segunda pública de reserva para poder rotar si la primera se pierde.
  3. Al publicar, herramientas/empaquetar.sh (en el Mac) firma el SHA-256 del zip junto con la versión (alcancia-actualizacion|<versión>|<sha256>) y deja Alcancia-macOS.zip.sig (base64) para subir al Release.
  4. La app baja zip, .sha256 y .sig con URLSession, calcula el SHA-256, verifica la firma con Curve25519.Signing.PublicKey.isValidSignature y revisa que la versión firmada sea la del Release y mayor que la instalada (así no se puede volver a una versión vieja con fallas). Solo si todo calza escribe el guion que reemplaza la app. Sin .sig válido: no actualiza y lo dice.
  5. Alternativa (o complemento) con costo: firmar con Developer ID de Apple y notarizar; el guion verificaría con codesign --verify --deep --strict -R '=anchor apple generic and certificate leaf[subject.OU] = "<TEAMID>"' y ya no haría falta quitar la cuarentena a ciegas.

Por qué no está implementado todavía: cambia el camino de instalación de la app, que no se puede compilar ni probar fuera de un Mac, y un actualizador roto no se puede arreglar con otra actualización. Se hace junto con el primer Release, probándolo en el Mac.


Security policy (English)

Report privately through Report a vulnerability (GitHub's private vulnerability reporting). Don't open a public issue. Never send real bank statements, API keys or personal data: build a fictitious sample instead.

We reply within 5 business days, share a plan within 30 days, and publish an advisory once a fix ships (crediting you if you wish); after 90 days without a fix we'll agree on disclosure with you. Good-faith research that doesn't touch other people's data is welcome and won't be pursued.

The Mac app is Pieza's generic «host-tauri» piece (Rust + Tauri): only the app's own window with its page in ui/ can use the bridge, every request is validated against acciones.json before anything runs (arguments are only values from a closed list or already-checked ids; the rest goes in an input file and the script checks it again; an absolute path is only accepted if it's the statement you picked), every script has a time limit, and keys live in the Keychain and reach scripts only through standard input. Updates come only from this repo's Releases, with their SHA-256 checked.

In scope: the Mac app (and future Windows/Linux apps), the phone app (movil/) and its encrypted copy, bin/ scripts, the ui/ interface, connectors, the MCP server, updates, the published site, GitHub workflows, and the recipe catalog. We especially care about code execution from statements, recipes, translations, transaction descriptions or AI replies; connector keys leaving the Keychain for anything but the service's official domain; forged updates; the AI seeing more than documented; and pull requests stealing workflow secrets or permissions. Out of scope: attacks that already require control of your computer or user account, issues in the AI tool or connected service themselves, and social engineering.

There aren't any published security advisories