## 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/` y `/my/events/`. - [ ] 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 `