RuscaDB es una base de datos embebida, in-process, multi-modelo y multimodal
escrita en Rust. Esta política describe cómo reportar vulnerabilidades, qué
versiones se soportan, el alcance del modelo de amenazas y la política de
unsafe.
El canon de referencia es OWASP (Top 10, ASVS, SAMM), MITRE CWE y las
prácticas de seguridad de GitHub (security advisories privados, Dependabot,
secret scanning, push protection). Las clases de debilidad relevantes para una
DB in-process se citan como CWE-XXXX.
Solo se aplican parches de seguridad a la última versión de la serie 0.1.x
(estado pre-1.0, API inestable).
| Versión | Soportada | Notas |
|---|---|---|
0.2.x |
❌ (futuro) | No publicada. |
0.1.x |
✅ | Serie actual. Se parchea la última 0.1.z. |
< 0.1 / main |
Sin garantía; se corrige si es trivial y ya está resuelto en 0.1.x. |
Antes de 1.0 el proyecto puede introducir cambios incompatibles en versiones
MINOR; los avisos de seguridad se publican siempre para la última 0.1.z.
NO abras un issue público. Un issue público expone la vulnerabilidad antes
de que exista un parche (coordinación de divulgación responsable / CWE-1059
deficiencias de documentación de seguridad).
Usa uno de estos canales privados:
- GitHub Security Advisory (preferido) — pestaña Security → Advisories → Report a vulnerability del repositorio (https://github.com/ruscadb/ruscadb/security/advisories/new). Permite discutir el reporte de forma privada y coordinar el CVE.
- Email —
security@ruscadb.dev. Si quieres cifrar el reporte, solicita primero la clave pública PGP en ese mismo buzón.
Incluye, en lo posible:
- versión de RuscaDB (
0.1.z) y plataforma/arquitectura; - el crate o capa afectada (
ruscadb-query,ruscadb-wal,ruscadb-ffi,ruscadb-vector,ruscadb-multimodal,ruscadb-crypto, …); - una descripción del impacto y un PoC mínimo (input, RQL, blob o llamada al C-ABI) que reproduzca el fallo;
- clasificación sugerida
CWE-XXXXy, si aplica, el mapeo OWASP; - si lo deseas, tu nombre/afiliación y si quieres crédito público.
| Etapa | Objetivo |
|---|---|
| Acuse de recibo | ≤ 3 días hábiles |
Triage inicial (severidad CVSS + CWE) |
≤ 7 días hábiles |
| Plan de mitigación / parche | Crítico/Alto ≤ 30 días; Medio/Bajo ≤ 90 días |
| Divulgación coordinada | Tras publicar el parche, o ≤ 90 días desde el reporte |
Estos plazos son objetivos de buena fe (proyecto pre-1.0, mantenido por voluntarios). Si el reporte está activamente explotado, se prioriza la mitigación inmediata.
RuscaDB corre en el mismo proceso que la aplicación anfitriona: no hay
frontera de proceso ni de red. Por tanto, un fallo de corrupción de memoria en
el parser, la deserialización o la FFI es un fallo de la app anfitriona (RCE o
corrupción de datos, CWE-119 memoria fuera de límites, CWE-416
use-after-free, CWE-787 escritura fuera de límites). «Es seguro por estar en
Rust» es falso: el unsafe acotado, la deserialización y el parser son la
superficie real.
| Superficie | Ejemplos de debilidad | Referencia |
|---|---|---|
| Parser RQL | Inyección por interpolación, DoS por complejidad (regex catastrófica, OR anidado), bypass del sandbox de file()/net |
CWE-89, CWE-1333, CWE-400 |
| Deserialización WAL / blob | parquet/Arrow malformado, offsets/lengths no validados, zip-bomb, torn writes |
CWE-119, CWE-787, CWE-20 |
FFI C-ABI (ruscadb-ffi) |
(ptr, len) inconsistente, doble free, use-after-free, panic que cruza el ABI |
CWE-119, CWE-416, CWE-787, CWE-617 |
| Índice vectorial HNSW / grafo CSR | OOB por lista de vecinos corrupta, get_unchecked sin invariantes |
CWE-119, CWE-787 |
Cifrado en reposo (ruscadb-crypto) |
Nonce reutilizado, AEAD sin verificación de tag, KDF débil, material sensible sin zeroize |
CWE-323, CWE-347, CWE-330, CWE-316 |
| Supply chain | Crate build.rs malicioso, dependencia comprometida, modelo .onnx malicioso |
CWE-1104, CWE-506 |
- Ataques que requieren acceso físico o root al host: cifrado en reposo protege el archivo en disco, no un adversario con control total de la máquina (keylogging, volcado de memoria del proceso vivo).
- La aplicación anfitriona: validación de identidad, autenticación, autorización de negocio, TLS/servidor y manejo de las claves que RuscaDB recibe. RuscaDB es una librería embebida: no expone red ni usuarios.
- Configuración insegura deliberada: capacidades
file/net/scripthabilitadas, presupuestos de recursos elevados o desactivar la verificación de integridad (CRC32C) por parte del integrador. - DoS de recursos cuando el anfitrión amplía los límites por encima de los
defaults seguros documentados (
docs/RuscaDB-roadmap.md§6.4). - Dependencias de terceros: se enrutan a su upstream (RustSec), aunque el
proyecto las rastrea con
cargo-audit/cargo-denyy SBOM.
El unsafe es el riesgo número uno de una DB in-process. La política es
release-blocking:
- Solo
ruscadb-ffihabilitaunsafe(#![allow(unsafe_code)]) porque su contrato es el C-ABI. Todos los demás crates lo prohíben con#![forbid(unsafe_code)]. - El presupuesto de bloques
unsafepor crate vive versionado enunsafe-allowlist.tomly lo verificascripts/check_core_unsafe.pyen el gate T1. Ningúnunsafenuevo entra sin ampliar el presupuesto en un PR revisado. - Cada bloque
unsafelleva un comentario// SAFETY:que justifica por qué las precondiciones se cumplen. Los lintsundocumented_unsafe_blocks,missing_safety_docymultiple_unsafe_ops_per_blockestán endeny;unsafe_op_in_unsafe_fnendeny. - La frontera C valida
(ptr, len)antes defrom_raw_parts/dereferenciar, usa una tabla de handles con marca mágica + generación (anti doble-free / use-after-free) y captura panics concatch_unwindpara que ninguno cruce el ABI. - Cobertura adicional:
miri(UB) en crates sinunsafe, AddressSanitizer (ASan) sobreruscadb-ffiycargo-fuzzsobre el parser RQL y la recuperación del WAL (.github/workflows/nightly.yml).
- Integridad: CRC32C por frame de WAL; frames manipulados se truncan o
rechazan. El manifiesto y el heap
.datano llevan checksum propio: su durabilidad depende de la escritura atómica (tmp+fsync+rename). - Datos en reposo (opcional): AEAD XChaCha20-Poly1305 sobre el WAL (el heap
.datahereda el cifrado), passphrase → Argon2id → clave de 256 bits,zeroizepara el material sensible. - Límites de recursos por defecto: profundidad AST 64, query budget
100 000, timeout 5 000 ms,LIMITimplícito 1 000 filas, blob 256 MiB, dimensión de vector ≤ 4 096, traversal depth 16. - Path safety:
file()restringido al sandbox root (canonicalización + prefijo; anti../symlink). - Supply chain:
Cargo.lock --locked,cargo-audit,cargo-deny, toolchain pinneada, SBOM CycloneDX por release ygitleaksen pre-commit/CI.
- Autenticación por defecto: el servidor MCP arranca sin sesión;
todo método de datos (
tools/*,resources/*) devuelve-32001hasta completar el handshakesession/authcon un token que solo el proceso padre puede emitir (McpServer::issue_session_token). Los mensajes de error jamás contienen el token ni material de key. - Tokens AEAD:
v1.<kid>.<nonce>.<ciphertext>— el payload (ruscadb-mcp-session-v1) se sella con XChaCha20-Poly1305 bajo la key activa del keystore; la validación es criptográfica y constante (tag Poly1305), sin estado en memoria del emisor. - Rotación = revocación:
rotate()retira la key activa; todos los tokens de esa key quedan invalidados en el acto (incluso en procesos posteriores si el keystore está en disco). No hay TTL: la rotación es el mecanismo de revocación. - KMS-lite (alcance local): el keystore es un fichero JSON
versionado (
version,kid,status) creado con permisos 0600 en unix. Las keys viven en claro a propósito: cifrarlas exigiría una passphrase fuera de alcance. Amenaza asumida: lectura local con permisos de fichero (mismo usuario). Amenaza fuera de alcance: KMS remoto/nube, rotación automática por tiempo, transporte HTTP/TLS. - Modelo MCP local: el transporte es stdio (sin red); la amenaza mitigada es un cliente local que hable por el stdio del hijo sin ser el padre que lo lanzó.
Gracias por ayudar a mantener RuscaDB seguro.