- renombrar openspec/ a _openspec/ (carpeta auxiliar) - mover specs de features a changes/ - crear specs base: arquitectura-limpia, estandares-codigo, nomenclatura - migrar _planificacion/ con design-system y roi-theme-template - agregar especificacion recaptcha anti-spam (proposal, tasks, spec) - corregir rutas y referencias en todas las specs Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
13 KiB
Especificación: reCAPTCHA v3 Anti-Spam Protection
Purpose
Define la integración de Google reCAPTCHA v3 para proteger los formularios del sitio (Newsletter y Contact Form) contra spam automatizado, siguiendo Clean Architecture.
Requirements
Requirement: Configuración de reCAPTCHA
El sistema DEBE permitir configurar reCAPTCHA v3 desde el panel de administración.
Scenario: Schema JSON para configuración
- WHEN se crea el schema de configuración
- THEN DEBE ubicarse en
Schemas/recaptcha-settings.json - AND
component_nameDEBE serrecaptcha-settings - AND DEBE incluir grupo VISIBILITY con
is_enabled - AND DEBE incluir grupo BEHAVIOR con
site_key,secret_key,score_threshold
Scenario: Campos obligatorios del schema
- GIVEN el schema
recaptcha-settings.json - WHEN se define la estructura
- THEN
is_enabledDEBE ser tipo boolean con default true - AND
site_keyDEBE ser tipo text (clave pública) - AND
secret_keyDEBE ser tipo text (clave secreta, almacenada encriptada) - AND
score_thresholdDEBE ser tipo select con options: 0.3, 0.5, 0.7, 0.9
Scenario: Sincronización con BD
- WHEN se sincroniza el schema
- THEN ejecutar
wp roi-theme sync-component recaptcha-settings - AND los datos DEBEN ir a
wp_roi_theme_component_settings - AND
component_nameen BD DEBE serrecaptcha-settings
Requirement: Contrato de Validación (Domain)
El Domain DEBE definir la interfaz de validación de reCAPTCHA.
Scenario: Ubicación de RecaptchaValidatorInterface
- WHEN se crea la interfaz
- THEN DEBE ubicarse en
Shared/Domain/Contracts/RecaptchaValidatorInterface.php - AND namespace DEBE ser
ROITheme\Shared\Domain\Contracts
Scenario: Firma del método validate
- WHEN se define RecaptchaValidatorInterface
- THEN DEBE tener método
validate(string $token, string $action): RecaptchaResult - AND
$tokenes el token generado por reCAPTCHA frontend - AND
$actiones el nombre de la acción (newsletter_submit, contact_submit) - AND retorna objeto
RecaptchaResultcon score y success
Scenario: Entidad RecaptchaResult
- WHEN se define el resultado de validación
- THEN DEBE existir
Shared/Domain/Entities/RecaptchaResult.php - AND DEBE tener propiedades:
success(bool),score(float),action(string),errorCodes(array) - AND DEBE tener método
isValid(float $threshold): bool
Requirement: Servicio de Aplicación
La capa Application DEBE orquestar la validación de reCAPTCHA.
Scenario: Ubicación del servicio
- WHEN se crea el servicio de aplicación
- THEN DEBE ubicarse en
Shared/Application/Services/RecaptchaValidationService.php - AND namespace DEBE ser
ROITheme\Shared\Application\Services
Scenario: Inyección de dependencias
- WHEN se implementa RecaptchaValidationService
- THEN DEBE inyectar
RecaptchaValidatorInterfacevia constructor - AND DEBE inyectar
ComponentSettingsRepositoryInterfacepara obtener configuración - AND NO DEBE instanciar servicios directamente con
new
Scenario: Lógica de validación con threshold
- GIVEN un token de reCAPTCHA y una acción
- WHEN se llama a
validateSubmission(string $token, string $action): bool - THEN DEBE obtener
score_thresholdde la configuración en BD - AND DEBE llamar a
RecaptchaValidatorInterface::validate() - AND DEBE retornar
truesiRecaptchaResult::isValid($threshold)es true - AND DEBE retornar
falsesi score está por debajo del threshold
Scenario: Bypass cuando está deshabilitado
- GIVEN
is_enabledes false en configuración - WHEN se llama a
validateSubmission() - THEN DEBE retornar
truesin llamar a la API de Google - AND NO DEBE generar errores
Requirement: Implementación de Infraestructura
La capa Infrastructure DEBE implementar la comunicación con API de Google.
Scenario: Ubicación del validador
- WHEN se implementa el validador
- THEN DEBE ubicarse en
Shared/Infrastructure/Services/GoogleRecaptchaValidator.php - AND namespace DEBE ser
ROITheme\Shared\Infrastructure\Services - AND DEBE implementar
RecaptchaValidatorInterface
Scenario: Llamada a API de Google
- WHEN se valida un token
- THEN DEBE hacer POST a
https://www.google.com/recaptcha/api/siteverify - AND DEBE enviar
secret(secret key) yresponse(token) - AND DEBE usar
wp_remote_post()de WordPress - AND timeout DEBE ser máximo 5 segundos
Scenario: Parseo de respuesta exitosa
- GIVEN API de Google responde exitosamente
- WHEN se parsea la respuesta
- THEN DEBE extraer
success(bool) - AND DEBE extraer
score(float 0.0-1.0) - AND DEBE extraer
action(string) - AND DEBE retornar
RecaptchaResultcon estos valores
Scenario: Manejo de errores de API
- GIVEN API de Google no responde o responde con error
- WHEN se procesa la respuesta
- THEN DEBE retornar
RecaptchaResultconsuccess = false - AND DEBE incluir códigos de error en
errorCodes - AND NO DEBE lanzar excepciones no controladas
Requirement: Integración Frontend
Los Renderers DEBEN incluir el script de reCAPTCHA y generar tokens.
Scenario: Script de reCAPTCHA en FooterRenderer
- WHEN FooterRenderer genera HTML del newsletter
- AND reCAPTCHA está habilitado
- THEN DEBE incluir script:
<script src="https://www.google.com/recaptcha/api.js?render={site_key}"></script> - AND DEBE agregar input hidden
recaptcha_tokenal formulario - AND DEBE agregar JS para ejecutar
grecaptcha.execute()al submit
Scenario: Script de reCAPTCHA en ContactFormRenderer
- WHEN ContactFormRenderer genera HTML del formulario
- AND reCAPTCHA está habilitado
- THEN DEBE incluir script de reCAPTCHA con site_key
- AND DEBE agregar input hidden
recaptcha_token - AND DEBE agregar JS para ejecutar
grecaptcha.execute()al submit
Scenario: JavaScript de ejecución de reCAPTCHA
- WHEN usuario hace submit del formulario
- THEN JS DEBE interceptar el submit
- AND DEBE llamar
grecaptcha.execute(siteKey, {action: 'action_name'}) - AND DEBE esperar el token (Promise)
- AND DEBE insertar token en input hidden
- AND DEBE continuar con el submit del formulario
Scenario: No cargar script si está deshabilitado
- GIVEN reCAPTCHA
is_enabledes false - WHEN se renderiza el formulario
- THEN NO DEBE incluir script de reCAPTCHA
- AND NO DEBE agregar input hidden de token
- AND formulario DEBE funcionar normalmente
Requirement: Integración Backend (AjaxHandlers)
Los AjaxHandlers DEBEN validar el token de reCAPTCHA antes de procesar.
Scenario: Validación en NewsletterAjaxHandler
- WHEN se procesa suscripción de newsletter
- AND reCAPTCHA está habilitado
- THEN DEBE obtener
recaptcha_tokendel POST - AND DEBE llamar a
RecaptchaValidationService::validateSubmission() - AND si retorna false, DEBE responder con error JSON
- AND si retorna true, DEBE continuar procesamiento normal
Scenario: Validación en ContactFormAjaxHandler
- WHEN se procesa envío de formulario de contacto
- AND reCAPTCHA está habilitado
- THEN DEBE obtener
recaptcha_tokendel POST - AND DEBE llamar a
RecaptchaValidationService::validateSubmission() - AND si retorna false, DEBE responder con error JSON
- AND si retorna true, DEBE continuar procesamiento normal
Scenario: Mensaje de error por reCAPTCHA fallido
- GIVEN validación de reCAPTCHA falla
- WHEN AjaxHandler responde
- THEN DEBE retornar JSON con
success: false - AND mensaje DEBE ser genérico: "No se pudo verificar que eres humano. Intenta de nuevo."
- AND NO DEBE revelar detalles técnicos del score
Scenario: Inyección de dependencias en AjaxHandlers
- WHEN se modifican los AjaxHandlers
- THEN DEBEN inyectar
RecaptchaValidationServicevia constructor - AND NO DEBE instanciar servicios directamente
- AND DEBE seguir principio de Inversión de Dependencias
Requirement: Panel de Administración
DEBE existir un FormBuilder para configurar reCAPTCHA.
Scenario: Ubicación del FormBuilder
- WHEN se crea el FormBuilder
- THEN DEBE ubicarse en
Admin/RecaptchaSettings/Infrastructure/Ui/RecaptchaSettingsFormBuilder.php - AND namespace DEBE ser
ROITheme\Admin\RecaptchaSettings\Infrastructure\Ui
Scenario: Registro en getComponents
- WHEN se registra el FormBuilder
- THEN DEBE registrarse en
getComponents()con IDrecaptcha-settings - AND DEBE aparecer en el menú del admin dashboard
Scenario: Campos del formulario admin
- WHEN se renderiza el formulario de configuración
- THEN DEBE mostrar toggle para
is_enabled - AND DEBE mostrar input text para
site_key - AND DEBE mostrar input password para
secret_key - AND DEBE mostrar select para
score_thresholdcon opciones 0.3, 0.5, 0.7, 0.9 - AND DEBE seguir Design System: gradiente #0E2337 → #1e3a5f, borde #FF8600
Scenario: FieldMapper para mapeo de campos
- WHEN se crea el componente admin
- THEN DEBE existir
Admin/RecaptchaSettings/Infrastructure/FieldMapping/RecaptchaSettingsFieldMapper.php - AND DEBE implementar
FieldMapperInterface - AND DEBE registrarse en
FieldMapperRegistry
Requirement: Seguridad
La implementación DEBE seguir mejores prácticas de seguridad.
Scenario: Almacenamiento de secret key
- WHEN se guarda el secret key
- THEN DEBE almacenarse encriptado en BD
- AND NO DEBE aparecer en código fuente
- AND NO DEBE exponerse en frontend
Scenario: Site key en frontend
- GIVEN site key es público por diseño de Google
- WHEN se incluye en frontend
- THEN PUEDE incluirse en atributo de script
- AND DEBE obtenerse de configuración en BD
Scenario: Validación de token en backend
- WHEN se recibe token de reCAPTCHA
- THEN DEBE sanitizarse con
sanitize_text_field() - AND DEBE validarse que no esté vacío
- AND DEBE enviarse a API de Google para verificación real
Scenario: No confiar solo en frontend
- GIVEN tokens pueden ser fabricados
- WHEN se valida reCAPTCHA
- THEN SIEMPRE DEBE verificarse con API de Google en backend
- AND NUNCA confiar en validación solo de frontend
Requirement: Logging y Monitoreo
El sistema DEBE registrar intentos de spam bloqueados.
Scenario: Log de intentos bloqueados
- WHEN reCAPTCHA bloquea un intento
- THEN DEBE registrar en log de WordPress
- AND DEBE incluir: timestamp, IP, action, score obtenido, threshold configurado
- AND NO DEBE incluir datos personales del usuario
Scenario: Log de errores de API
- WHEN API de Google falla
- THEN DEBE registrar error en log
- AND DEBE incluir código de error y mensaje
- AND DEBE permitir diagnóstico del problema
Requirement: Fallback y Resiliencia
El sistema DEBE manejar fallos graciosamente.
Scenario: Fail-open cuando API no responde
- GIVEN API de Google no responde (timeout)
- WHEN se intenta validar
- THEN DEBE permitir el envío del formulario (fail-open)
- AND DEBE registrar el evento en log
- AND NO DEBE bloquear usuarios legítimos por falla de terceros
Scenario: Degradación cuando reCAPTCHA deshabilitado
- GIVEN administrador deshabilita reCAPTCHA
- WHEN se envía formulario
- THEN DEBE procesarse normalmente
- AND validaciones existentes (nonce, rate limit) DEBEN seguir activas
- AND NO DEBE generar errores por ausencia de token
Checklist de Implementación
Archivos a Crear
Schemas/recaptcha-settings.jsonShared/Domain/Contracts/RecaptchaValidatorInterface.phpShared/Domain/Entities/RecaptchaResult.phpShared/Application/Services/RecaptchaValidationService.phpShared/Infrastructure/Services/GoogleRecaptchaValidator.phpAdmin/RecaptchaSettings/Infrastructure/Ui/RecaptchaSettingsFormBuilder.phpAdmin/RecaptchaSettings/Infrastructure/FieldMapping/RecaptchaSettingsFieldMapper.php
Archivos a Modificar
Public/Footer/Infrastructure/Ui/FooterRenderer.phpPublic/Footer/Infrastructure/Api/WordPress/NewsletterAjaxHandler.phpPublic/ContactForm/Infrastructure/Ui/ContactFormRenderer.phpPublic/ContactForm/Infrastructure/Api/WordPress/ContactFormAjaxHandler.phpfunctions.php(registro DI)Admin/Infrastructure/Ui/AdminDashboardRenderer.php(registrar componente)Admin/Shared/Infrastructure/FieldMapping/FieldMapperRegistry.php
Validaciones
- Schema tiene campos de visibilidad
- Domain no tiene dependencias de Infrastructure
- Application solo depende de interfaces de Domain
- Todos los servicios inyectados via constructor
- CSS generado via CSSGeneratorService (si aplica)
- Secret key nunca expuesto en frontend
Última actualización
2025-01-08