## 1. Pre-flight y preparación (reglas de compromiso)

- [ ] 1.1 Redactar y firmar la autorización (alcance, IPs origen Kali+localhost, ventana, contacto) y guardarla como cabecera de `docs/SECURITY_FINDINGS.md`.
- [ ] 1.2 Tomar snapshot/backup de BD y volumen; probar la restauración. Preparar clon de staging.
- [ ] 1.3 Crear cuentas de test dedicadas (`sec_test_portal`, `sec_test_user`, opcional manager); anotar grupos y partners.
- [ ] 1.4 Confirmar topología de prod vía SSH (`ss -tlnp`, `ps aux | grep odoo`, config nginx/traefik, `odoo.conf`, fail2ban, firewall, versión exacta de Odoo).

## 2. Entregables de documentación (`docs/`)

- [ ] 2.1 Crear `docs/SECURITY_AUDIT_CHECKLIST.md`: checklist de prevuelo con ítems markables por los 3 frentes + infra, cada uno con criterio pass/fail.
- [ ] 2.2 Crear `docs/SECURITY_AUDIT_RUNBOOK.md`: comandos paso a paso de la auditoría activa (nmap, hydra/patator, testssl.sh, nikto, ffuf, invocación de scripts) con las salvaguardas de la sección 1.
- [ ] 2.3 Crear `docs/SECURITY_FINDINGS.md`: registro con severidad/evidencia/estado, sembrado con H1–H5 + hallazgos de infra.

## 3. Scripts de auditoría (`scripts/security/`, no destructivos, credenciales por parámetro/env)

- [ ] 3.1 `odoo_acl_probe.py`: autentica como portal/usuario de test vía API externa y prueba matriz read/write/create/unlink sobre `group.order`, `group.order.slot`, `product.supplierinfo`, `res.partner`, `res.users`, `account.move`, `sale.order` (de otro partner), `ir.config_parameter`, `ir.attachment`, `account.banking.mandate`, `res.partner.bank`.
- [ ] 3.2 `idor_probe.py`: como portal de test, itera `order_id`/`group_order_id` en `load_eskaera_page`, `load_products_ajax`, `add_to_eskaera_cart`, `eskaera_checkout`, `check_group_order_status` y detecta fuga de catálogo/precios de grupos ajenos; incluir IDOR vertical sobre `/my/orders/<id>` y `/my/events/<id>`.
- [ ] 3.3 `bruteforce_test.sh`: hydra/patator controlado contra cuenta de test en `/web/login` y contra la ruta API (`/web/session/authenticate`, `/xmlrpc/2/common`); verifica baneo de la IP de Kali por fail2ban.
- [ ] 3.4 `csrf_poc.html`: formulario cross-site que fuerza `POST /eskaera/clear-cart` y `/eskaera/confirm` desde sesión logueada (para demostrar H3 antes del fix).
- [ ] 3.5 `web_recon.sh`: nmap + testssl.sh + nikto + ffuf sobre rutas Odoo conocidas (`/web/database/manager`, `/web/database/selector`, `?debug=1`, `/web/webclient/version_info`, `.git/`) y chequeo de cabeceras/cookies.

## 4. Ejecución de la auditoría activa (registrar todo en el findings register)

- [ ] 4.1 Frente 1 (fuerza bruta): ejecutar 3.3; medir umbral/tiempo de baneo web y API; revisar política de contraseñas (`auth_password_policy`), 2FA (`auth_totp`), `auth_signup` no invitado, gestor de BD.
- [ ] 4.2 Frente 2 (acceso a datos vía API): ejecutar 3.1 y 3.2 (baseline pre-fix); documentar accesos indebidos (valida H1, H2, H4).
- [ ] 4.3 Frente 3 (vectores inadvertidos): ejecutar 3.4 (valida H3), 3.5 (TLS, cabeceras, superficie web, versión/CVEs) y revisar `t-raw`/XSS (valida H5).

## 5. Fixes de código — `website_sale_aplicoop`

- [ ] 5.1 (H1) En `security/ir.model.access.csv`: sustituir la fila `access_group_order_base` (grupo vacío) por lectura interna `base.group_user` `1,0,0,0`; idem `access_group_order_slot_base`; mantener filas de manager para escritura; añadir ACL de portal de solo lectura para el slot solo si el controlador lo lee sin `sudo()`.
- [ ] 5.2 (H2) Identificar en plantillas/controladores qué campos de `product.supplierinfo` lee el portal (origen del producto, proveedor principal vía `product.seller_ids`) y confirmar que el pricing va por `sudo()` en `controllers/website_sale_pricing.py`.
- [ ] 5.3 (H2, preferido) Preparar origen + proveedor principal en el controlador vía `sudo()` y pasarlos resueltos a la plantilla (sin lógica en QWeb); luego eliminar la ACL de portal de `product.supplierinfo` (fila 6 del CSV) y la record rule `rule_product_supplierinfo_portal_read`.
- [ ] 5.4 (H2, fallback si el render no se puede mover al controlador) Sustituir `domain=[(1,'=',1)]` de `rule_product_supplierinfo_portal_read` por un dominio acotado a los productos de los grupos del usuario, sin exponer campos de coste/precio.
- [ ] 5.5 (H3) En `controllers/website_sale.py`: convertir `save-order`, `confirm`, `clear-cart`, `save-cart` de `type="http"`+`csrf=False` a `type="json"` (plantilla: `confirm_order_from_portal`); devolver `dict`.
- [ ] 5.6 (H3) Ajustar el JS de `static/src/js/` (solo transporte: fetch con envelope JSON-RPC y lectura de `result`), sin lógica de negocio en JS.
- [ ] 5.7 (H4) En `controllers/website_sale.py`: añadir comprobación de pertenencia tras `exists()`/`state` en `load_eskaera_page`, `load_products_ajax`, `add_to_eskaera_cart`, `eskaera_checkout`, `check_group_order_status`, reutilizando `_get_consumer_group_for_user` (retorno vacío/redirect) o `_validate_user_group_access` (fallo duro); mantener bypass de usuario interno (`share == False`).
- [ ] 5.8 (H5) En `views/load_from_history_templates.xml`: sustituir `t-raw` en `<script>` por `<script type="application/json">` leído por el JS.
- [ ] 5.9 Bump de versión en `website_sale_aplicoop/__manifest__.py` (`18.0.X.Y.Z`).

## 6. Fix versionable de infra — `account_banking_mandate_batch`

- [ ] 6.1 Añadir `groups_id` a la server action de mandatos SEPA en `account_banking_mandate_batch/data/server_action.xml` para restringir quién puede lanzarla.

## 7. Tests (extender los existentes de `website_sale_aplicoop/tests/`)

- [ ] 7.1 `test_record_rules.py` / `test_multi_company.py`: aserciones de que el portal NO puede write/create `group.order` ni `group.order.slot` (H1) ni leer `product.supplierinfo` ajeno (H2).
- [ ] 7.2 `test_group_order_status_endpoint.py`: un portal fuera del grupo NO obtiene datos de los endpoints de lectura/ajax (H4); un miembro sí.
- [ ] 7.3 Test de que los endpoints de estado convertidos a `type="json"` funcionan same-origin y que el flujo de checkout/carrito sigue operando (H3).

## 8. Hardening de infra (servidor de prod; no versionable — verificar contra el checklist)

- [ ] 8.1 `odoo.conf`: `admin_passwd` fuerte, `list_db=False`, `dbfilter` por host, `proxy_mode=True`, `workers>0`, `limit_*`, `without_demo=True`, `db_password` fuerte; log parseable por fail2ban.
- [ ] 8.2 nginx/traefik: `limit_req` en `/web/login`, `/web/session/authenticate`, `/jsonrpc`, `/xmlrpc`; bloquear/restringir `/web/database/*`; HSTS + cabeceras; TLS fuerte; cookie `session_id` Secure/HttpOnly/SameSite; upgrade de `/websocket`.
- [ ] 8.3 fail2ban: jails para login web y API (IP real vía `proxy_mode`), jail de 429 de nginx y `recidive`; verificar baneo con 3.3.
- [ ] 8.4 BD/secretos: confirmar 5432 no expuesto por firewall; usuario de BD con mínimos privilegios; externalizar secretos fuera de ficheros versionados.

## 9. Verificación y calidad

- [ ] 9.1 Re-ejecutar 3.1/3.2 (deben denegar lo que antes permitían) y 3.4 (debe fallar tras el fix); actualizar el findings register a resuelto/verificado.
- [ ] 9.2 Tests del addon: `docker-compose run odoo odoo -d odoo --test-enable --stop-after-init -u website_sale_aplicoop` (usar `run`, no `exec`).
- [ ] 9.3 Verificación end-to-end del portal (carrito, save-order, confirm, clear-cart, checkout) tras la conversión a `type="json"`.
- [ ] 9.4 Calidad: `make format` / `make lint` (black línea 88 + isort + flake8 + pylint-odoo); sin `_()` en definiciones de campo; `pre-commit run --all-files`.
- [ ] 9.5 Cerrar cada ítem del checklist de prevuelo con evidencia.
