movidas openspec a /docs

This commit is contained in:
GitHub Copilot 2026-08-07 16:46:33 +02:00
parent 0ef0c9951c
commit aae6b6096e
9 changed files with 486 additions and 0 deletions

View file

@ -0,0 +1,106 @@
# Proposal — Auditoría de seguridad y hardening (Odoo 18.0)
## Why
Detectamos ataques automatizados cada vez más sofisticados que la heurística de fail2ban no frena,
y somos responsables de la seguridad de nuestra infra y la de nuestros clientes. Necesitamos (a) un
proceso de auditoría **reproducible** tipo "checklist de prevuelo" para verificar la resiliencia
ante fuerza bruta, acceso indebido a datos vía API y vectores inadvertidos; y (b) corregir cinco
hallazgos ya confirmados leyendo el propio código de `website_sale_aplicoop`.
## Objetivo
Dejar la infra Odoo con una postura de seguridad auditable y repetible, y cerrar los hallazgos
H1H5 con tests que impidan su regresión.
## Alcance
- **Solo nuestra infra** (autorización directa, sin terceros). Producción está tras nginx/traefik
con TLS.
- Auditoría **activa autorizada** (fuerza bruta controlada contra cuenta de test y fuzzing de
endpoints) desde la máquina Kali de la red y desde `localhost` vía SSH, preferentemente contra un
clon de staging o en ventana de mantenimiento con snapshot previo.
- Remediación de los hallazgos confirmados en `website_sale_aplicoop` (+ `groups_id` en la server
action de mandatos SEPA de `account_banking_mandate_batch`).
## No-objetivos
- No se audita ni escanea infra de terceros ni hosting ajeno.
- No se corrige aquí ningún hallazgo **nuevo** que surja durante la auditoría activa: se registra en
el findings register y se aborda en un change posterior (salvo severidad crítica, que se escalará).
- No se modifica `ocb/` ni los addons OCA originales.
## What Changes
- **Proceso de auditoría**: se añaden entregables de documentación (`docs/SECURITY_AUDIT_CHECKLIST.md`,
`docs/SECURITY_AUDIT_RUNBOOK.md`, `docs/SECURITY_FINDINGS.md`) y scripts no destructivos en
`scripts/security/` (probe de ACL vía API, probe de IDOR, harness de fuerza bruta, PoC de CSRF,
recon web) parametrizados con cuentas de test.
- **Fix H1 (Alta)** — ACL con `group_id` vacío que concede write+create global sobre `group.order` y
`group.order.slot`: se reemplaza por ACLs de mínimo privilegio (lectura interna/portal explícitas;
escritura solo manager). Los writes de los controladores usan `sudo()` y no dependen de estas ACLs.
- **Fix H2 (Alta)** — record rule de `product.supplierinfo` con `domain=[(1,'=',1)]` que expone todos
los precios de proveedor al portal. El portal usa `supplierinfo` para pintar el **origen** del
producto y el **proveedor principal** en la web, así que no se elimina sin más: o se preparan esos
valores server-side (vía `sudo()` en el controlador, según la regla "sin lógica en QWeb") o se acota
el dominio a los productos accesibles por el usuario, sin exponer campos de coste/precio.
- **Fix H3 (Media)** — endpoints de cambio de estado con `csrf=False`: se convierten a `type="json"`
(bloquea el POST de formulario cross-site), tomando como plantilla `confirm_order_from_portal`.
- **Fix H4 (Media)** — endpoints de lectura/ajax que solo comprueban `exists()`+`state`: se añade
comprobación de pertenencia al grupo reutilizando `_validate_user_group_access` /
`_get_consumer_group_for_user`.
- **Fix H5 (Baja)**`t-raw` inyectado en `<script>`: se sustituye por un patrón de salida seguro.
- **Hardening de config**: `admin_passwd`, `list_db=False`, `proxy_mode=True`, password de BD fuerte,
rate-limit/cabeceras en nginx, jails de fail2ban (web + API). Documentado como requisitos; los
cambios de infra no versionable se ejecutan en el servidor, los versionables (`groups_id` en la
server action) en el repo.
## Capabilities
### New Capabilities
- `eskaera-access-control`: cómo `website_sale_aplicoop` restringe acceso a datos y acciones —
ACL de mínimo privilegio, alcance de `product.supplierinfo`, autorización por pertenencia al grupo
de consumo en todos los endpoints, protección CSRF en endpoints de estado, y escapado de salida en
plantillas. Cubre H1H5.
- `security-audit-process`: proceso repetible de auditoría — reglas de compromiso/autorización,
checklist de prevuelo, runbook de auditoría activa, scripts de prueba y registro de hallazgos.
- `infra-hardening`: requisitos de endurecimiento de producción — `odoo.conf`, reverse proxy
(nginx/traefik) con TLS/rate-limit/cabeceras, fail2ban (web + API), y gestión de secretos/BD.
### Modified Capabilities
- (ninguna — no existen specs previas en `openspec/specs/`)
## Impact
- **Addons afectados**: `website_sale_aplicoop` (security CSV + record rules, controladores,
plantilla, tests, bump de `__manifest__.py`); `account_banking_mandate_batch` (`groups_id` en la
server action).
- **Addons NO afectados**: `ocb/`, los addons OCA originales, y el resto de custom (solo se auditan;
cualquier hallazgo se registra, no se modifica en este change).
- **Nuevas rutas en el repo**: `docs/SECURITY_*`, `scripts/security/`.
- **Impacto UI/website/QWeb**: al convertir endpoints a `type="json"` hay que ajustar el transporte
en el JS de `website_sale_aplicoop` (solo transporte, sin lógica). La plantilla
`load_from_history_templates.xml` cambia su forma de inyectar JSON. Sin cambios visibles de UX.
- **Infra (no versionable)**: `odoo.conf`, config de nginx/traefik y fail2ban en el servidor de prod.
## Riesgos
- La auditoría activa (fuerza bruta/fuzzing) puede degradar o afectar a producción: mitigado con
clon de staging o ventana de mantenimiento, snapshot previo y cuentas de test dedicadas (nunca
contra cuentas reales, por riesgo de lockout/DoS de usuarios legítimos).
- Convertir endpoints a `type="json"` cambia el contrato de request/response: riesgo de regresión en
el carrito/checkout del portal; mitigado con tests y verificación end-to-end.
- Recortar el acceso a `product.supplierinfo` podría romper la visualización de precios si algún
punto del portal dependía de la ACL abierta: verificar que el pricing se computa vía `sudo()`.
## Criterios de aceptación
- Los probes `odoo_acl_probe.py` e `idor_probe.py` deniegan tras los fixes lo que permitían antes
(write/create de `group.order`, lectura de `supplierinfo` ajeno, datos de grupos ajenos).
- El PoC de CSRF funciona antes del fix y falla después.
- El harness de fuerza bruta acaba con la IP de Kali baneada por fail2ban en `/web/login` y en la
ruta API.
- Tests de `website_sale_aplicoop` en verde:
`docker-compose run odoo odoo -d odoo --test-enable --stop-after-init -u website_sale_aplicoop`.
- El checklist de prevuelo cubre los tres frentes + infra, con criterio pass/fail por ítem, y el
findings register queda con H1H5 en estado resuelto/verificado.