@saulwade/swl-ses 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (702) hide show
  1. package/CLAUDE.md +238 -0
  2. package/README.md +560 -0
  3. package/_userland/agentes/.gitkeep +0 -0
  4. package/_userland/habilidades/.gitkeep +0 -0
  5. package/agentes/.evolved.json +9 -0
  6. package/agentes/accesibilidad-wcag-swl.md +692 -0
  7. package/agentes/arquitecto-swl.md +238 -0
  8. package/agentes/auto-evolucion-swl.md +854 -0
  9. package/agentes/backend-api-swl.md +470 -0
  10. package/agentes/backend-csharp-swl.md +418 -0
  11. package/agentes/backend-go-swl.md +388 -0
  12. package/agentes/backend-java-swl.md +279 -0
  13. package/agentes/backend-node-swl.md +477 -0
  14. package/agentes/backend-python-swl.md +608 -0
  15. package/agentes/backend-rust-swl.md +362 -0
  16. package/agentes/backend-workers-swl.md +480 -0
  17. package/agentes/cloud-infra-swl.md +485 -0
  18. package/agentes/consolidador-swl.md +539 -0
  19. package/agentes/datos-swl.md +584 -0
  20. package/agentes/depurador-swl.md +349 -0
  21. package/agentes/devops-ci-swl.md +374 -0
  22. package/agentes/disenador-ui-swl.md +558 -0
  23. package/agentes/documentador-swl.md +343 -0
  24. package/agentes/evals/arquitecto-swl.evals.json +56 -0
  25. package/agentes/evals/auto-evolucion-swl.evals.json +68 -0
  26. package/agentes/evals/implementador-swl.evals.json +56 -0
  27. package/agentes/evals/orquestador-swl.evals.json +60 -0
  28. package/agentes/evals/perfilador-usuario-swl.evals.json +60 -0
  29. package/agentes/evals/red-team-swl.evals.json +59 -0
  30. package/agentes/evals/revisor-codigo-swl.evals.json +59 -0
  31. package/agentes/frontend-angular-swl.md +627 -0
  32. package/agentes/frontend-css-swl.md +720 -0
  33. package/agentes/frontend-react-swl.md +696 -0
  34. package/agentes/frontend-swl.md +500 -0
  35. package/agentes/frontend-tailwind-swl.md +830 -0
  36. package/agentes/implementador-swl.md +328 -0
  37. package/agentes/investigador-swl.md +430 -0
  38. package/agentes/investigador-ux-swl.md +500 -0
  39. package/agentes/llm-apps-swl.md +276 -0
  40. package/agentes/migrador-swl.md +417 -0
  41. package/agentes/mobile-android-swl.md +509 -0
  42. package/agentes/mobile-cross-swl.md +539 -0
  43. package/agentes/mobile-ios-swl.md +500 -0
  44. package/agentes/mobile-testing-swl.md +300 -0
  45. package/agentes/notificador-swl.md +916 -0
  46. package/agentes/observabilidad-swl.md +436 -0
  47. package/agentes/orquestador-swl.md +884 -0
  48. package/agentes/pagos-swl.md +283 -0
  49. package/agentes/perfilador-usuario-swl.md +306 -0
  50. package/agentes/planificador-swl.md +402 -0
  51. package/agentes/producto-prd-swl.md +587 -0
  52. package/agentes/red-team-swl.md +216 -0
  53. package/agentes/release-manager-swl.md +568 -0
  54. package/agentes/rendimiento-swl.md +714 -0
  55. package/agentes/resolutor-build-swl.md +243 -0
  56. package/agentes/revisor-angular-swl.md +276 -0
  57. package/agentes/revisor-codigo-swl.md +348 -0
  58. package/agentes/revisor-csharp-swl.md +262 -0
  59. package/agentes/revisor-go-swl.md +257 -0
  60. package/agentes/revisor-java-swl.md +255 -0
  61. package/agentes/revisor-kotlin-swl.md +271 -0
  62. package/agentes/revisor-nextjs-swl.md +279 -0
  63. package/agentes/revisor-php-swl.md +269 -0
  64. package/agentes/revisor-react-swl.md +276 -0
  65. package/agentes/revisor-rust-swl.md +344 -0
  66. package/agentes/revisor-seguridad-swl.md +390 -0
  67. package/agentes/revisor-swift-swl.md +266 -0
  68. package/agentes/revisor-typescript-swl.md +344 -0
  69. package/agentes/sre-swl.md +265 -0
  70. package/agentes/tdd-qa-swl.md +354 -0
  71. package/agentes/ux-disenador-swl.md +501 -0
  72. package/bin/lib/bot-comandos.js +1030 -0
  73. package/bin/lib/bot-discovery.js +182 -0
  74. package/bin/lib/bot-git.js +142 -0
  75. package/bin/swl-ses.js +325 -0
  76. package/bin/swl-telegram-bot.js +442 -0
  77. package/bin/swl-telegram-bot.plist +21 -0
  78. package/bin/swl-telegram-bot.service +14 -0
  79. package/comandos/swl/.evolved.json +23 -0
  80. package/comandos/swl/actualizar.md +174 -0
  81. package/comandos/swl/adoptar-proyecto.md +207 -0
  82. package/comandos/swl/aprender.md +701 -0
  83. package/comandos/swl/auditar-deps.md +134 -0
  84. package/comandos/swl/autoresearch.md +170 -0
  85. package/comandos/swl/ayuda.md +224 -0
  86. package/comandos/swl/brainstorm.md +50 -0
  87. package/comandos/swl/checkpoint.md +330 -0
  88. package/comandos/swl/compactar.md +283 -0
  89. package/comandos/swl/configurar-ci.md +227 -0
  90. package/comandos/swl/contexto.md +112 -0
  91. package/comandos/swl/contribuir.md +233 -0
  92. package/comandos/swl/crear-skill.md +292 -0
  93. package/comandos/swl/cron.md +196 -0
  94. package/comandos/swl/dashboard.md +146 -0
  95. package/comandos/swl/discutir-fase.md +230 -0
  96. package/comandos/swl/ejecutar-fase.md +135 -0
  97. package/comandos/swl/evaluar-skill.md +487 -0
  98. package/comandos/swl/evolucion-estado.md +142 -0
  99. package/comandos/swl/evolucionar.md +259 -0
  100. package/comandos/swl/exportar-vault.md +189 -0
  101. package/comandos/swl/gateway.md +158 -0
  102. package/comandos/swl/inbox.md +116 -0
  103. package/comandos/swl/instalar.md +220 -0
  104. package/comandos/swl/instintos.md +86 -0
  105. package/comandos/swl/mapear-codebase.md +312 -0
  106. package/comandos/swl/mcp-status.md +175 -0
  107. package/comandos/swl/metricas.md +270 -0
  108. package/comandos/swl/modelo.md +102 -0
  109. package/comandos/swl/notificaciones.md +396 -0
  110. package/comandos/swl/nuevo-proyecto.md +154 -0
  111. package/comandos/swl/planear-fase.md +221 -0
  112. package/comandos/swl/plugins.md +256 -0
  113. package/comandos/swl/reflect-skills.md +125 -0
  114. package/comandos/swl/release.md +217 -0
  115. package/comandos/swl/revisar-impacto.md +206 -0
  116. package/comandos/swl/revisar.md +330 -0
  117. package/comandos/swl/salud.md +363 -0
  118. package/comandos/swl/sesiones.md +200 -0
  119. package/comandos/swl/skill-search.md +113 -0
  120. package/comandos/swl/verificar.md +585 -0
  121. package/comandos/swl/wiki.md +620 -0
  122. package/contextos/dev.md +32 -0
  123. package/contextos/research.md +30 -0
  124. package/contextos/review.md +31 -0
  125. package/habilidades/accesibilidad-a11y/SKILL.md +201 -0
  126. package/habilidades/accesibilidad-a11y/evals/evals.json +56 -0
  127. package/habilidades/accesibilidad-a11y/recursos/ejemplos-y-checklist-completo.md +441 -0
  128. package/habilidades/agent-browser/SKILL.md +218 -0
  129. package/habilidades/agentes-como-servicio/SKILL.md +218 -0
  130. package/habilidades/ai-runtime-security/SKILL.md +273 -0
  131. package/habilidades/angular-avanzado/SKILL.md +164 -0
  132. package/habilidades/angular-avanzado/recursos/ejemplos-avanzados.md +219 -0
  133. package/habilidades/angular-moderno/SKILL.md +186 -0
  134. package/habilidades/angular-moderno/evals/evals.json +45 -0
  135. package/habilidades/angular-moderno/recursos/ejemplos-avanzados.md +106 -0
  136. package/habilidades/api-rest-diseno/SKILL.md +191 -0
  137. package/habilidades/api-rest-diseno/recursos/openapi-template.yaml +506 -0
  138. package/habilidades/api-rest-diseno/recursos/referencia-api.md +140 -0
  139. package/habilidades/aprendizaje-continuo/SKILL.md +151 -0
  140. package/habilidades/aprendizaje-continuo/evals/evals.json +53 -0
  141. package/habilidades/aprendizaje-continuo/recursos/referencia-instintos.md +290 -0
  142. package/habilidades/async-python/SKILL.md +149 -0
  143. package/habilidades/async-python/evals/evals.json +47 -0
  144. package/habilidades/async-python/recursos/patrones-y-ejemplos-completos.md +292 -0
  145. package/habilidades/auth-patrones/.evolved.json +9 -0
  146. package/habilidades/auth-patrones/SKILL.md +413 -0
  147. package/habilidades/auth-patrones/recursos/implementaciones-completas.md +229 -0
  148. package/habilidades/auto-evolucion-protocolo/SKILL.md +276 -0
  149. package/habilidades/auto-evolucion-protocolo/evals/evals.json +55 -0
  150. package/habilidades/auto-evolucion-protocolo/recursos/referencia-completa.md +145 -0
  151. package/habilidades/autoresearch/SKILL.md +268 -0
  152. package/habilidades/autoresearch/evals/evals.json +41 -0
  153. package/habilidades/autoresearch/recursos/checklist-template.md +191 -0
  154. package/habilidades/autoresearch/scripts/calcular-score.js +88 -0
  155. package/habilidades/azure-cloud/SKILL.md +308 -0
  156. package/habilidades/azure-cloud/recursos/aks.md +327 -0
  157. package/habilidades/backend-mcp-servidor/SKILL.md +270 -0
  158. package/habilidades/backend-production-resilience/SKILL.md +288 -0
  159. package/habilidades/brainstorming/SKILL.md +295 -0
  160. package/habilidades/brainstorming/recursos/componentes-html.md +247 -0
  161. package/habilidades/build-errors-cpp/SKILL.md +270 -0
  162. package/habilidades/build-errors-csharp/SKILL.md +265 -0
  163. package/habilidades/build-errors-go/SKILL.md +306 -0
  164. package/habilidades/build-errors-java/SKILL.md +278 -0
  165. package/habilidades/build-errors-kotlin/SKILL.md +303 -0
  166. package/habilidades/build-errors-nextjs/SKILL.md +312 -0
  167. package/habilidades/build-errors-php/SKILL.md +270 -0
  168. package/habilidades/build-errors-python/SKILL.md +292 -0
  169. package/habilidades/build-errors-rust/SKILL.md +284 -0
  170. package/habilidades/build-errors-swift/SKILL.md +272 -0
  171. package/habilidades/build-errors-typescript/SKILL.md +369 -0
  172. package/habilidades/checklist-calidad/SKILL.md +271 -0
  173. package/habilidades/checklist-calidad/recursos/quality-report-template.md +148 -0
  174. package/habilidades/checklist-seguridad/SKILL.md +285 -0
  175. package/habilidades/checkpoints-verificacion/SKILL.md +298 -0
  176. package/habilidades/checkpoints-verificacion/recursos/checkpoint-templates.md +360 -0
  177. package/habilidades/ci-cd-pipelines/SKILL.md +157 -0
  178. package/habilidades/ci-cd-pipelines/recursos/github-actions-template.yaml +403 -0
  179. package/habilidades/ci-cd-pipelines/recursos/pipelines-completos.md +487 -0
  180. package/habilidades/cloud-aws/SKILL.md +142 -0
  181. package/habilidades/cloud-aws/recursos/servicios-aws-referencia.md +321 -0
  182. package/habilidades/compactacion-contexto/SKILL.md +247 -0
  183. package/habilidades/contenedores-docker/SKILL.md +137 -0
  184. package/habilidades/contenedores-docker/recursos/dockerfile-template.dockerfile +160 -0
  185. package/habilidades/contenedores-docker/recursos/ejemplos-y-configuraciones.md +327 -0
  186. package/habilidades/context-builder/SKILL.md +170 -0
  187. package/habilidades/control-profundidad/SKILL.md +128 -0
  188. package/habilidades/csharp-experto/SKILL.md +322 -0
  189. package/habilidades/csharp-patrones/SKILL.md +316 -0
  190. package/habilidades/csharp-testing/SKILL.md +286 -0
  191. package/habilidades/css-moderno/SKILL.md +166 -0
  192. package/habilidades/css-moderno/evals/evals.json +43 -0
  193. package/habilidades/css-moderno/recursos/ejemplos-y-patrones-completos.md +337 -0
  194. package/habilidades/datos-etl/SKILL.md +129 -0
  195. package/habilidades/datos-etl/recursos/implementaciones-completas.md +322 -0
  196. package/habilidades/dbml-experto/SKILL.md +339 -0
  197. package/habilidades/dbml-experto/evals/evals.json +56 -0
  198. package/habilidades/dependencias-auditoria/SKILL.md +320 -0
  199. package/habilidades/deprecacion-migracion/SKILL.md +169 -0
  200. package/habilidades/deprecacion-migracion/recursos/implementaciones-completas.md +220 -0
  201. package/habilidades/design-tokens/SKILL.md +158 -0
  202. package/habilidades/design-tokens/recursos/tokens-y-configuracion.md +363 -0
  203. package/habilidades/devsecops-pipeline-security/SKILL.md +309 -0
  204. package/habilidades/diagrama-arquitectura/SKILL.md +165 -0
  205. package/habilidades/diagrama-arquitectura/assets/template.html +276 -0
  206. package/habilidades/discutir-fase/SKILL.md +188 -0
  207. package/habilidades/diseno-herramientas-agente/SKILL.md +199 -0
  208. package/habilidades/diseno-responsivo/SKILL.md +186 -0
  209. package/habilidades/diseno-responsivo/recursos/ejemplos-layouts.md +156 -0
  210. package/habilidades/django-experto/SKILL.md +205 -0
  211. package/habilidades/django-experto/recursos/async-django.md +390 -0
  212. package/habilidades/django-experto/recursos/drf-patrones.md +438 -0
  213. package/habilidades/django-experto/recursos/orm-avanzado.md +382 -0
  214. package/habilidades/django-experto/recursos/referencia-completa.md +188 -0
  215. package/habilidades/django-experto/recursos/testing-django.md +415 -0
  216. package/habilidades/doc-sync/SKILL.md +280 -0
  217. package/habilidades/drift-detection/SKILL.md +179 -0
  218. package/habilidades/ejecutar-fase/SKILL.md +468 -0
  219. package/habilidades/estilo-sin-ai-isms/SKILL.md +775 -0
  220. package/habilidades/estilo-sin-ai-isms/evals/evals.json +63 -0
  221. package/habilidades/estilo-sin-ai-isms/scripts/detectar_aiisms.py +500 -0
  222. package/habilidades/estructura-proyecto-claude/SKILL.md +215 -0
  223. package/habilidades/estructura-proyecto-claude/recursos/claude-md-template.md +261 -0
  224. package/habilidades/estructura-proyecto-claude/recursos/configuracion-y-extensiones.md +176 -0
  225. package/habilidades/estructura-proyecto-claude/recursos/frontmatter-y-hooks-referencia.md +289 -0
  226. package/habilidades/estructura-proyecto-claude/recursos/mcp-json-template.json +77 -0
  227. package/habilidades/estructura-proyecto-claude/recursos/variantes-por-stack.md +177 -0
  228. package/habilidades/evaluacion-agentes/SKILL.md +314 -0
  229. package/habilidades/event-driven/SKILL.md +153 -0
  230. package/habilidades/event-driven/recursos/implementaciones-completas.md +423 -0
  231. package/habilidades/extraccion-documentos/SKILL.md +221 -0
  232. package/habilidades/extractor-de-aprendizajes/.evolved.json +9 -0
  233. package/habilidades/extractor-de-aprendizajes/SKILL.md +311 -0
  234. package/habilidades/extractor-de-aprendizajes/evals/evals.json +55 -0
  235. package/habilidades/fastapi-experto/SKILL.md +221 -0
  236. package/habilidades/fastapi-experto/recursos/async-patterns.md +438 -0
  237. package/habilidades/fastapi-experto/recursos/dependency-injection.md +330 -0
  238. package/habilidades/fastapi-experto/recursos/referencia-completa.md +79 -0
  239. package/habilidades/fastapi-experto/recursos/testing-httpx.md +420 -0
  240. package/habilidades/filament-admin/SKILL.md +290 -0
  241. package/habilidades/frontend-avanzado/SKILL.md +257 -0
  242. package/habilidades/frontend-avanzado/recursos/apis-nativas-ejemplos.md +341 -0
  243. package/habilidades/gcp-cloud/SKILL.md +260 -0
  244. package/habilidades/gcp-cloud/recursos/gke.md +234 -0
  245. package/habilidades/gcp-cloud/recursos/terraform-gcp.md +307 -0
  246. package/habilidades/generacion-mermaid/SKILL.md +229 -0
  247. package/habilidades/git-worktrees-paralelo/SKILL.md +270 -0
  248. package/habilidades/go-experto/SKILL.md +305 -0
  249. package/habilidades/go-patrones/SKILL.md +299 -0
  250. package/habilidades/go-testing/SKILL.md +291 -0
  251. package/habilidades/graphql-experto/SKILL.md +323 -0
  252. package/habilidades/guardrail-semantico/SKILL.md +282 -0
  253. package/habilidades/harness-claude-code/SKILL.md +299 -0
  254. package/habilidades/iam-secretos/SKILL.md +265 -0
  255. package/habilidades/iam-secretos/recursos/implementaciones-completas.md +356 -0
  256. package/habilidades/infra-github-actions/SKILL.md +166 -0
  257. package/habilidades/instalar-sistema/.evolved.json +9 -0
  258. package/habilidades/instalar-sistema/SKILL.md +221 -0
  259. package/habilidades/java-experto/SKILL.md +290 -0
  260. package/habilidades/java-patrones/SKILL.md +275 -0
  261. package/habilidades/java-testing/SKILL.md +288 -0
  262. package/habilidades/kotlin-compose/SKILL.md +278 -0
  263. package/habilidades/kotlin-compose/recursos/animaciones-performance.md +93 -0
  264. package/habilidades/kotlin-experto/SKILL.md +318 -0
  265. package/habilidades/kotlin-testing/SKILL.md +267 -0
  266. package/habilidades/kotlin-testing/recursos/testing-avanzado.md +74 -0
  267. package/habilidades/kubernetes-orquestacion/SKILL.md +152 -0
  268. package/habilidades/kubernetes-orquestacion/recursos/manifiestos-completos.md +452 -0
  269. package/habilidades/langchain-langraph/SKILL.md +386 -0
  270. package/habilidades/langchain-langraph/recursos/evaluacion-rag.md +321 -0
  271. package/habilidades/langchain-langraph/recursos/rag-maturity-model.md +225 -0
  272. package/habilidades/langchain-langraph/recursos/vectorstores.md +306 -0
  273. package/habilidades/legacy-code-rescue/SKILL.md +267 -0
  274. package/habilidades/likec4-experto/SKILL.md +412 -0
  275. package/habilidades/likec4-experto/evals/evals.json +69 -0
  276. package/habilidades/manejo-errores/.evolved.json +9 -0
  277. package/habilidades/manejo-errores/SKILL.md +407 -0
  278. package/habilidades/manejo-errores/recursos/implementaciones-completas.md +248 -0
  279. package/habilidades/mapear-codebase/SKILL.md +275 -0
  280. package/habilidades/memoria-busqueda/SKILL.md +194 -0
  281. package/habilidades/memoria-busqueda/evals/evals.json +44 -0
  282. package/habilidades/meta-skills-estandar/SKILL.md +298 -0
  283. package/habilidades/meta-skills-estandar/recursos/anti-patrones-y-leyes.md +205 -0
  284. package/habilidades/meta-skills-estandar/recursos/frameworks-seguridad.md +107 -0
  285. package/habilidades/meta-skills-estandar/recursos/idiomas-framework.md +60 -0
  286. package/habilidades/meta-skills-estandar/recursos/skills-as-agents.md +163 -0
  287. package/habilidades/microservicios/SKILL.md +155 -0
  288. package/habilidades/microservicios/recursos/patrones-y-ejemplos-completos.md +325 -0
  289. package/habilidades/mobile-flutter/SKILL.md +199 -0
  290. package/habilidades/mobile-flutter/recursos/ejemplos-completos.md +319 -0
  291. package/habilidades/mobile-react-native/SKILL.md +176 -0
  292. package/habilidades/mobile-react-native/recursos/ejemplos-completos.md +216 -0
  293. package/habilidades/mongodb-experto/SKILL.md +302 -0
  294. package/habilidades/monitoring-alertas/SKILL.md +201 -0
  295. package/habilidades/monitoring-alertas/recursos/instrumentacion-y-alertas.md +301 -0
  296. package/habilidades/nestjs-experto/SKILL.md +307 -0
  297. package/habilidades/nestjs-experto/recursos/guards-interceptors.md +339 -0
  298. package/habilidades/nestjs-experto/recursos/modulos-di.md +287 -0
  299. package/habilidades/nestjs-experto/recursos/testing-nestjs.md +354 -0
  300. package/habilidades/nextjs-experto/SKILL.md +335 -0
  301. package/habilidades/nextjs-patrones/SKILL.md +303 -0
  302. package/habilidades/nextjs-testing/SKILL.md +331 -0
  303. package/habilidades/node-experto/.evolved.json +9 -0
  304. package/habilidades/node-experto/SKILL.md +266 -0
  305. package/habilidades/node-experto/recursos/patrones-completos.md +283 -0
  306. package/habilidades/notificaciones-multicanal/SKILL.md +159 -0
  307. package/habilidades/notificaciones-multicanal/recursos/config-template.json +115 -0
  308. package/habilidades/notificaciones-multicanal/recursos/configuracion-y-templates.md +303 -0
  309. package/habilidades/nuevo-proyecto/SKILL.md +204 -0
  310. package/habilidades/orquestacion-async/SKILL.md +303 -0
  311. package/habilidades/paid-media-tracking/SKILL.md +269 -0
  312. package/habilidades/paid-media-tracking/recursos/auditoria-tracking.md +220 -0
  313. package/habilidades/paid-media-tracking/recursos/google-ads-api.md +215 -0
  314. package/habilidades/patrones-python/SKILL.md +228 -0
  315. package/habilidades/patrones-python/evals/evals.json +56 -0
  316. package/habilidades/patrones-python/recursos/patrones-avanzados.md +469 -0
  317. package/habilidades/patrones-python/recursos/referencia-completa.md +202 -0
  318. package/habilidades/perfil-usuario/SKILL.md +200 -0
  319. package/habilidades/perfil-usuario/evals/evals.json +55 -0
  320. package/habilidades/performance-baseline/SKILL.md +297 -0
  321. package/habilidades/php-experto/SKILL.md +291 -0
  322. package/habilidades/php-patrones/SKILL.md +306 -0
  323. package/habilidades/php-testing/SKILL.md +280 -0
  324. package/habilidades/planear-fase/SKILL.md +269 -0
  325. package/habilidades/postgresql-experto/SKILL.md +151 -0
  326. package/habilidades/postgresql-experto/evals/evals.json +53 -0
  327. package/habilidades/postgresql-experto/recursos/referencia-completa.md +215 -0
  328. package/habilidades/prevencion-racionalizacion/SKILL.md +175 -0
  329. package/habilidades/prevencion-sobreingenieria/SKILL.md +323 -0
  330. package/habilidades/privacy-memoria/SKILL.md +141 -0
  331. package/habilidades/privacy-memoria/evals/evals.json +43 -0
  332. package/habilidades/prompt-engineering/SKILL.md +518 -0
  333. package/habilidades/prompt-engineering/recursos/patrones-avanzados.md +467 -0
  334. package/habilidades/rag-arquitectura/SKILL.md +338 -0
  335. package/habilidades/rails-experto/SKILL.md +237 -0
  336. package/habilidades/rails-experto/recursos/active-record.md +260 -0
  337. package/habilidades/rails-experto/recursos/hotwire-turbo.md +293 -0
  338. package/habilidades/rails-experto/recursos/testing-rspec.md +362 -0
  339. package/habilidades/react-experto/SKILL.md +209 -0
  340. package/habilidades/react-experto/evals/evals.json +55 -0
  341. package/habilidades/react-experto/recursos/patrones-y-ejemplos-completos.md +240 -0
  342. package/habilidades/react-optimizacion/SKILL.md +174 -0
  343. package/habilidades/react-optimizacion/recursos/patrones-avanzados.md +138 -0
  344. package/habilidades/redis-experto/SKILL.md +305 -0
  345. package/habilidades/release-semver/.evolved.json +9 -0
  346. package/habilidades/release-semver/SKILL.md +248 -0
  347. package/habilidades/release-semver/scripts/generar-changelog.sh +238 -0
  348. package/habilidades/rust-experto/SKILL.md +400 -0
  349. package/habilidades/rust-patrones/SKILL.md +296 -0
  350. package/habilidades/rust-testing/SKILL.md +311 -0
  351. package/habilidades/seguridad-skills-ia/SKILL.md +262 -0
  352. package/habilidades/sql-optimizacion/SKILL.md +200 -0
  353. package/habilidades/sql-optimizacion/evals/evals.json +54 -0
  354. package/habilidades/sql-optimizacion/recursos/patrones-sql-avanzados.md +131 -0
  355. package/habilidades/sre-patrones/SKILL.md +333 -0
  356. package/habilidades/sre-patrones/recursos/chaos-engineering.md +241 -0
  357. package/habilidades/sre-patrones/recursos/oncall-design.md +236 -0
  358. package/habilidades/stripe-pagos/SKILL.md +550 -0
  359. package/habilidades/stripe-pagos/recursos/errores-reintentos.md +390 -0
  360. package/habilidades/stripe-pagos/recursos/stripe-connect.md +290 -0
  361. package/habilidades/structured-outputs/SKILL.md +343 -0
  362. package/habilidades/swift-experto/SKILL.md +320 -0
  363. package/habilidades/swift-experto/recursos/keychain-y-wrappers.md +110 -0
  364. package/habilidades/swift-patrones/SKILL.md +313 -0
  365. package/habilidades/swift-patrones/recursos/tca-ejemplo-completo.md +113 -0
  366. package/habilidades/swift-testing/SKILL.md +254 -0
  367. package/habilidades/swift-testing/recursos/xcuitest-planes.md +143 -0
  368. package/habilidades/swl-dashboard/SKILL.md +370 -0
  369. package/habilidades/swl-markitdown/SKILL.md +285 -0
  370. package/habilidades/swl-markitdown/evals/evals.json +52 -0
  371. package/habilidades/swl-revisar-impacto/SKILL.md +233 -0
  372. package/habilidades/tailwind-experto/SKILL.md +240 -0
  373. package/habilidades/tailwind-experto/recursos/referencia-completa.md +184 -0
  374. package/habilidades/tdd-workflow/SKILL.md +293 -0
  375. package/habilidades/terraform-experto/SKILL.md +321 -0
  376. package/habilidades/testing-python/SKILL.md +340 -0
  377. package/habilidades/testing-python/recursos/ejemplos-completos.md +167 -0
  378. package/habilidades/threat-model-lite/SKILL.md +246 -0
  379. package/habilidades/tracing-processor/SKILL.md +212 -0
  380. package/habilidades/tracking-measurement/SKILL.md +239 -0
  381. package/habilidades/tracking-measurement/recursos/consent-mode.md +231 -0
  382. package/habilidades/tracking-measurement/recursos/gtm-datalayer.md +216 -0
  383. package/habilidades/tracking-measurement/recursos/meta-capi.md +262 -0
  384. package/habilidades/typescript-avanzado/SKILL.md +144 -0
  385. package/habilidades/typescript-avanzado/evals/evals.json +55 -0
  386. package/habilidades/typescript-avanzado/recursos/patrones-y-ejemplos-completos.md +298 -0
  387. package/habilidades/typescript-diagnosticos/SKILL.md +513 -0
  388. package/habilidades/ux-diseno/SKILL.md +116 -0
  389. package/habilidades/ux-diseno/evals/evals.json +43 -0
  390. package/habilidades/ux-diseno/recursos/patrones-ux-referencia.md +214 -0
  391. package/habilidades/validacion-ci-sistema/SKILL.md +136 -0
  392. package/habilidades/validacion-ci-sistema/recursos/validadores-completos.md +369 -0
  393. package/habilidades/validacion-ci-sistema/scripts/validar-sistema.sh +286 -0
  394. package/habilidades/verificacion-evidencia/SKILL.md +160 -0
  395. package/habilidades/verificar-trabajo/SKILL.md +303 -0
  396. package/habilidades/verificar-trabajo/recursos/plantilla-verificacion.md +60 -0
  397. package/habilidades/wiki-conocimiento/SKILL.md +276 -0
  398. package/habilidades/wireframes-flujos/SKILL.md +212 -0
  399. package/habilidades/wireframes-flujos/recursos/referencia-completa.md +192 -0
  400. package/habilidades/workflow-claude-code/SKILL.md +260 -0
  401. package/habilidades/workflow-claude-code/recursos/referencia-completa.md +109 -0
  402. package/hooks/_run-hook.sh +57 -0
  403. package/hooks/actualizar-perfil-usuario.js +364 -0
  404. package/hooks/agente-lifecycle.js +71 -0
  405. package/hooks/aiisms-detector.js +173 -0
  406. package/hooks/audit-trail.js +204 -0
  407. package/hooks/auto-background.js +97 -0
  408. package/hooks/auto-consolidacion.js +178 -0
  409. package/hooks/auto-evolucion.js +666 -0
  410. package/hooks/auto-restaurar-settings.js +360 -0
  411. package/hooks/calidad-pre-commit.js +929 -0
  412. package/hooks/calidad-typescript.js +511 -0
  413. package/hooks/captura-feedback-usuario.js +148 -0
  414. package/hooks/check-update.js +211 -0
  415. package/hooks/clasificador-mensajes.js +271 -0
  416. package/hooks/degradacion-instintos.js +272 -0
  417. package/hooks/escaneo-secretos.js +389 -0
  418. package/hooks/extraccion-aprendizajes.js +763 -0
  419. package/hooks/grafo-contexto.js +129 -0
  420. package/hooks/graph-update.js +67 -0
  421. package/hooks/guardrail-modelo.js +247 -0
  422. package/hooks/inbox-aviso.js +75 -0
  423. package/hooks/inyeccion-contexto.js +246 -0
  424. package/hooks/lib/abort-registry.js +214 -0
  425. package/hooks/lib/agent-backend.js +210 -0
  426. package/hooks/lib/agent-comms.js +263 -0
  427. package/hooks/lib/agent-issue-codes.js +284 -0
  428. package/hooks/lib/agent-matcher.js +189 -0
  429. package/hooks/lib/async-hook-registry.js +252 -0
  430. package/hooks/lib/atomic-write.js +130 -0
  431. package/hooks/lib/auto-consolidator.js +335 -0
  432. package/hooks/lib/canary-skills.js +187 -0
  433. package/hooks/lib/consolidation-lock.js +291 -0
  434. package/hooks/lib/context-builder.js +430 -0
  435. package/hooks/lib/context-compressor.js +657 -0
  436. package/hooks/lib/convergence-detector.js +105 -0
  437. package/hooks/lib/delegation-tracker.js +198 -0
  438. package/hooks/lib/detectar-package-manager.js +423 -0
  439. package/hooks/lib/edit-accumulator.js +171 -0
  440. package/hooks/lib/error-classifier.js +308 -0
  441. package/hooks/lib/event-bus.js +112 -0
  442. package/hooks/lib/evolution-tracker.js +442 -0
  443. package/hooks/lib/execution-state.js +316 -0
  444. package/hooks/lib/fingerprint-id.js +135 -0
  445. package/hooks/lib/gateway-notify.js +116 -0
  446. package/hooks/lib/graph-security.js +75 -0
  447. package/hooks/lib/guardrail-metrics.js +202 -0
  448. package/hooks/lib/hook-circuit-breaker.js +206 -0
  449. package/hooks/lib/loop-detector.js +267 -0
  450. package/hooks/lib/mcp-health.js +184 -0
  451. package/hooks/lib/mcp-pool.js +436 -0
  452. package/hooks/lib/memory-search.js +506 -0
  453. package/hooks/lib/merkle-audit.js +96 -0
  454. package/hooks/lib/model-router.js +222 -0
  455. package/hooks/lib/normalize-error.js +324 -0
  456. package/hooks/lib/normalize-input.js +65 -0
  457. package/hooks/lib/nudge-tracker.js +306 -0
  458. package/hooks/lib/otlp-exporter.js +365 -0
  459. package/hooks/lib/performance-marks.js +239 -0
  460. package/hooks/lib/privacy-filter.js +128 -0
  461. package/hooks/lib/prompt-injection-scanner.js +209 -0
  462. package/hooks/lib/provenance-tracker.js +183 -0
  463. package/hooks/lib/rate-limit-tracker.js +253 -0
  464. package/hooks/lib/reflect-classifier.js +164 -0
  465. package/hooks/lib/resource-quota.js +122 -0
  466. package/hooks/lib/retry-jitter.js +165 -0
  467. package/hooks/lib/risk-engine.js +368 -0
  468. package/hooks/lib/run-log.js +408 -0
  469. package/hooks/lib/session-fts.js +379 -0
  470. package/hooks/lib/session-store.js +293 -0
  471. package/hooks/lib/singleton-guard.js +159 -0
  472. package/hooks/lib/skill-auditor.js +588 -0
  473. package/hooks/lib/sync-status.js +228 -0
  474. package/hooks/lib/taint-tracker.js +107 -0
  475. package/hooks/lib/task-service.js +295 -0
  476. package/hooks/lib/tech-skills-map.js +146 -0
  477. package/hooks/lib/telegram-cliente.js +159 -0
  478. package/hooks/lib/telegram-config.js +170 -0
  479. package/hooks/lib/token-budget.js +156 -0
  480. package/hooks/lib/token-estimator.js +420 -0
  481. package/hooks/lib/toon-compressor.js +245 -0
  482. package/hooks/lib/usage-model.js +183 -0
  483. package/hooks/lib/variable-resolver.js +230 -0
  484. package/hooks/linea-estado.js +324 -0
  485. package/hooks/metricas-evolucion.js +209 -0
  486. package/hooks/monitor-contexto.js +325 -0
  487. package/hooks/notificacion-sesion-stop.js +198 -0
  488. package/hooks/notificacion-telegram-notification.js +4 -0
  489. package/hooks/notificacion-telegram-subagent.js +4 -0
  490. package/hooks/notificacion-telegram.js +267 -0
  491. package/hooks/preservar-estado-pre-compact.js +150 -0
  492. package/hooks/proteccion-rutas.js +366 -0
  493. package/hooks/registro-turnos.js +209 -0
  494. package/hooks/resumen-sesion.js +249 -0
  495. package/hooks/risk-scoring.js +323 -0
  496. package/hooks/rotar-audit-auto.js +122 -0
  497. package/hooks/sugerir-regenerar-inventario.js +170 -0
  498. package/hooks/telemetria-agentes.js +167 -0
  499. package/hooks/tracking-costos.js +688 -0
  500. package/instintos/global.yaml +8 -0
  501. package/instintos/perfil-usuario.yaml +53 -0
  502. package/instintos/prompt-appendices.yaml +57 -0
  503. package/instintos/proyecto.yaml +372 -0
  504. package/manifiestos/gateway-config.json +77 -0
  505. package/manifiestos/handoff-context.json +223 -0
  506. package/manifiestos/hook-profiles.json +44 -0
  507. package/manifiestos/hooks-config.json +360 -0
  508. package/manifiestos/modulos.json +1173 -0
  509. package/manifiestos/perfiles.json +404 -0
  510. package/package.json +86 -0
  511. package/plantillas/ESTADO.md +109 -0
  512. package/plantillas/HOJA-RUTA.md +143 -0
  513. package/plantillas/PROYECTO.md +122 -0
  514. package/plantillas/REQUISITOS.md +132 -0
  515. package/plantillas/auditor-veto-template.md +105 -0
  516. package/plantillas/github-workflows/README.md +47 -0
  517. package/plantillas/github-workflows/release-please.yml +44 -0
  518. package/plantillas/github-workflows/swl-ci.yml +107 -0
  519. package/plantillas/github-workflows/swl-security.yml +51 -0
  520. package/plantillas/mcp-mineru.json +13 -0
  521. package/plantillas/research/ARQUITECTURA.md +220 -0
  522. package/plantillas/research/FUNCIONALIDADES.md +175 -0
  523. package/plantillas/research/RESUMEN.md +165 -0
  524. package/plantillas/research/STACK.md +233 -0
  525. package/plantillas/research/TRAMPAS.md +299 -0
  526. package/plantillas/skill-evals-template.json +44 -0
  527. package/plugin.json +343 -0
  528. package/reglas/accesibilidad.md +269 -0
  529. package/reglas/api-diseno.md +400 -0
  530. package/reglas/arquitectura.md +352 -0
  531. package/reglas/brevedad-output.md +124 -0
  532. package/reglas/cloud-infra.md +247 -0
  533. package/reglas/docs.md +245 -0
  534. package/reglas/estilo-codigo.md +201 -0
  535. package/reglas/git-workflow.md +245 -0
  536. package/reglas/gobernanza.md +271 -0
  537. package/reglas/harness-claude-code.md +213 -0
  538. package/reglas/hooks.md +186 -0
  539. package/reglas/lenguajes/csharp/estilo-codigo.md +231 -0
  540. package/reglas/lenguajes/csharp/hooks.md +281 -0
  541. package/reglas/lenguajes/csharp/patrones.md +226 -0
  542. package/reglas/lenguajes/csharp/seguridad.md +258 -0
  543. package/reglas/lenguajes/csharp/testing.md +176 -0
  544. package/reglas/lenguajes/go/estilo-codigo.md +195 -0
  545. package/reglas/lenguajes/go/hooks.md +249 -0
  546. package/reglas/lenguajes/go/patrones.md +249 -0
  547. package/reglas/lenguajes/go/seguridad.md +225 -0
  548. package/reglas/lenguajes/go/testing.md +272 -0
  549. package/reglas/lenguajes/java/estilo-codigo.md +217 -0
  550. package/reglas/lenguajes/java/hooks.md +251 -0
  551. package/reglas/lenguajes/java/patrones.md +226 -0
  552. package/reglas/lenguajes/java/seguridad.md +233 -0
  553. package/reglas/lenguajes/java/testing.md +238 -0
  554. package/reglas/lenguajes/kotlin/estilo-codigo.md +208 -0
  555. package/reglas/lenguajes/kotlin/hooks.md +245 -0
  556. package/reglas/lenguajes/kotlin/patrones.md +201 -0
  557. package/reglas/lenguajes/kotlin/seguridad.md +202 -0
  558. package/reglas/lenguajes/kotlin/testing.md +236 -0
  559. package/reglas/lenguajes/nextjs/estilo-codigo.md +175 -0
  560. package/reglas/lenguajes/nextjs/hooks.md +186 -0
  561. package/reglas/lenguajes/nextjs/patrones.md +225 -0
  562. package/reglas/lenguajes/nextjs/seguridad.md +216 -0
  563. package/reglas/lenguajes/nextjs/testing.md +193 -0
  564. package/reglas/lenguajes/php/estilo-codigo.md +228 -0
  565. package/reglas/lenguajes/php/hooks.md +165 -0
  566. package/reglas/lenguajes/php/patrones.md +233 -0
  567. package/reglas/lenguajes/php/seguridad.md +186 -0
  568. package/reglas/lenguajes/php/testing.md +205 -0
  569. package/reglas/lenguajes/rust/estilo-codigo.md +207 -0
  570. package/reglas/lenguajes/rust/hooks.md +240 -0
  571. package/reglas/lenguajes/rust/patrones.md +250 -0
  572. package/reglas/lenguajes/rust/seguridad.md +221 -0
  573. package/reglas/lenguajes/rust/testing.md +194 -0
  574. package/reglas/lenguajes/swift/estilo-codigo.md +238 -0
  575. package/reglas/lenguajes/swift/hooks.md +257 -0
  576. package/reglas/lenguajes/swift/patrones.md +235 -0
  577. package/reglas/lenguajes/swift/seguridad.md +248 -0
  578. package/reglas/lenguajes/swift/testing.md +242 -0
  579. package/reglas/markitdown.md +60 -0
  580. package/reglas/memoria-consolidada.md +209 -0
  581. package/reglas/patrones.md +225 -0
  582. package/reglas/performance.md +195 -0
  583. package/reglas/pruebas.md +159 -0
  584. package/reglas/seguridad-agentes.md +351 -0
  585. package/reglas/seguridad.md +151 -0
  586. package/reglas/skills-estandar.md +373 -0
  587. package/reglas/testing.md +193 -0
  588. package/schemas/agent-contract.json +176 -0
  589. package/schemas/agent-frontmatter.schema.json +149 -0
  590. package/schemas/agent-message.schema.json +53 -0
  591. package/schemas/agent-output-implementacion.schema.json +85 -0
  592. package/schemas/agent-output-planificacion.schema.json +113 -0
  593. package/schemas/agent-output-review.schema.json +78 -0
  594. package/schemas/diary-entry.schema.json +80 -0
  595. package/schemas/hook-profiles.schema.json +39 -0
  596. package/schemas/hooks-config.schema.json +74 -0
  597. package/schemas/instinct.schema.json +115 -0
  598. package/schemas/modulos.schema.json +29 -0
  599. package/schemas/perfiles.schema.json +28 -0
  600. package/schemas/plugin.schema.json +64 -0
  601. package/schemas/skill-evals.schema.json +95 -0
  602. package/schemas/skill-frontmatter.schema.json +170 -0
  603. package/scripts/actualizar.js +145 -0
  604. package/scripts/audit-skills.sh +78 -0
  605. package/scripts/auditar-agentes-gaps.js +149 -0
  606. package/scripts/auditar-cobertura-frameworks.js +241 -0
  607. package/scripts/auditar-skills-gaps.js +206 -0
  608. package/scripts/bootstrap-instintos.js +259 -0
  609. package/scripts/check-update.js +109 -0
  610. package/scripts/comandos/agents.js +105 -0
  611. package/scripts/comandos/info.js +108 -0
  612. package/scripts/comandos/install-asistido.js +186 -0
  613. package/scripts/comandos/skills.js +211 -0
  614. package/scripts/configurar-branch-protection.js +418 -0
  615. package/scripts/daemon-swl.py +388 -0
  616. package/scripts/desinstalar.js +130 -0
  617. package/scripts/doctor.js +559 -0
  618. package/scripts/field-report.js +199 -0
  619. package/scripts/generar-inventario.js +317 -0
  620. package/scripts/inbox-tmux-inject.js +161 -0
  621. package/scripts/inferir-herramientas-permitidas.js +586 -0
  622. package/scripts/inicializar.js +133 -0
  623. package/scripts/instalador.js +1031 -0
  624. package/scripts/instalar-git-hook.js +122 -0
  625. package/scripts/lib/agp-frontmatter.js +222 -0
  626. package/scripts/lib/append-con-marcadores.js +199 -0
  627. package/scripts/lib/artefactos-python.js +43 -0
  628. package/scripts/lib/audit-query.js +221 -0
  629. package/scripts/lib/autostart-linux.js +347 -0
  630. package/scripts/lib/autostart-macos.js +360 -0
  631. package/scripts/lib/autostart-windows.js +307 -0
  632. package/scripts/lib/budget-enforcer.js +252 -0
  633. package/scripts/lib/claude-sessions.js +285 -0
  634. package/scripts/lib/configurar-ci.js +380 -0
  635. package/scripts/lib/console-span-exporter.js +92 -0
  636. package/scripts/lib/contadores-inventario.js +217 -0
  637. package/scripts/lib/dashboard-widgets.js +290 -0
  638. package/scripts/lib/detectar-runtime.js +279 -0
  639. package/scripts/lib/detectar-stack.js +187 -0
  640. package/scripts/lib/diary-entry.js +234 -0
  641. package/scripts/lib/drift-detector.js +545 -0
  642. package/scripts/lib/estado.js +124 -0
  643. package/scripts/lib/gestor-componentes.js +243 -0
  644. package/scripts/lib/gitignore-manifest.js +305 -0
  645. package/scripts/lib/graph-analyze.py +556 -0
  646. package/scripts/lib/graph-builder.py +485 -0
  647. package/scripts/lib/graph-cluster.py +259 -0
  648. package/scripts/lib/health-row.js +168 -0
  649. package/scripts/lib/hooks-settings.js +789 -0
  650. package/scripts/lib/manifiestos.js +138 -0
  651. package/scripts/lib/mc-client.js +137 -0
  652. package/scripts/lib/notificaciones-telegram.js +1107 -0
  653. package/scripts/lib/npm-version.js +261 -0
  654. package/scripts/lib/paquetes-conocidos.js +50 -0
  655. package/scripts/lib/preservar-usuario.js +586 -0
  656. package/scripts/lib/prompt-builder.js +264 -0
  657. package/scripts/lib/resolver-externo.js +332 -0
  658. package/scripts/lib/schedule-parser.js +305 -0
  659. package/scripts/lib/scoring-instintos.js +240 -0
  660. package/scripts/lib/seguridad.js +160 -0
  661. package/scripts/lib/selector-interactivo.js +152 -0
  662. package/scripts/lib/semantic-search.js +242 -0
  663. package/scripts/lib/skill-discovery.js +234 -0
  664. package/scripts/lib/skill-metrics.js +246 -0
  665. package/scripts/lib/skill-normalizer.js +112 -0
  666. package/scripts/lib/skills-hub.js +340 -0
  667. package/scripts/lib/span-schema.js +134 -0
  668. package/scripts/lib/tool-cost-analyzer.js +255 -0
  669. package/scripts/lib/tracing-processor-interface.js +286 -0
  670. package/scripts/lib/transformadores/base.js +80 -0
  671. package/scripts/lib/transformadores/claude.js +124 -0
  672. package/scripts/lib/transformadores/codex.js +115 -0
  673. package/scripts/lib/transformadores/copilot.js +106 -0
  674. package/scripts/lib/transformadores/gemini.js +74 -0
  675. package/scripts/lib/transformadores/index.js +35 -0
  676. package/scripts/lib/transformadores/opencode.js +75 -0
  677. package/scripts/lib/ui.js +259 -0
  678. package/scripts/limpiar-artefactos-python.js +131 -0
  679. package/scripts/mcp-orchestrator.py +386 -0
  680. package/scripts/mcp-pool-manager.py +352 -0
  681. package/scripts/mcp-telemetry.py +378 -0
  682. package/scripts/poblar-evolvable.js +226 -0
  683. package/scripts/publicar.js +287 -0
  684. package/scripts/reflect-skills.js +403 -0
  685. package/scripts/rotar-audit-logs.js +185 -0
  686. package/scripts/run-skill-evals.js +242 -0
  687. package/scripts/smoke-test.js +374 -0
  688. package/scripts/token-analysis.py +471 -0
  689. package/scripts/validar-manifest.js +195 -0
  690. package/scripts/validar-memoria.js +321 -0
  691. package/scripts/validar-tests-aislamiento.js +184 -0
  692. package/scripts/validar-tokens-test.js +208 -0
  693. package/scripts/validar.js +147 -0
  694. package/scripts/validate-markdown.py +339 -0
  695. package/scripts/validate-skills.py +385 -0
  696. package/scripts/vendor/claude-usage/README.md +116 -0
  697. package/scripts/vendor/claude-usage/cli.py +334 -0
  698. package/scripts/vendor/claude-usage/dashboard.py +795 -0
  699. package/scripts/vendor/claude-usage/scanner.py +467 -0
  700. package/scripts/vendor/markitdown/cli.py +194 -0
  701. package/scripts/verificar-evolucion.js +289 -0
  702. package/scripts/verificar-release.js +494 -0
@@ -0,0 +1,413 @@
1
+ ---
2
+ name: auth-patrones
3
+ description: Patrones de autenticación y autorización. JWT, OAuth2, OIDC, RBAC, ABAC, session management, refresh tokens, PKCE, MFA. Implementaciones en FastAPI y Django. Reglas anti-error de seguridad críticas.
4
+ version: "1.2.0"
5
+ evolved: true
6
+ evolved-from: "1.1.0"
7
+ evolved-at: "2026-04-24"
8
+ evolved-by: "aprender"
9
+ evolved-note: "2 secciones nuevas: IDOR defense con 404 (no 403) + consent PII en TODOS los ambientes"
10
+ herramientasPermitidas: [Read, Grep]
11
+ nist_csf: [PR.AA-01, PR.AA-02, PR.AA-03, PR.AA-04, PR.AA-05]
12
+ attack_techniques: [T1078, T1110, T1212]
13
+ d3fend_techniques: [D3-MFA]
14
+ exclusiones:
15
+ - "No cargar para seguridad de infraestructura (firewall, VPN, TLS, certificados) — este skill cubre autenticación de aplicación, no seguridad de red."
16
+ - "No cargar para gestión de secretos en cloud (AWS Secrets Manager, Vault, Azure Key Vault) — para infraestructura de secretos cargar `cloud-infra`."
17
+ - "No cargar para auditorías de seguridad de código o OWASP Top 10 completo — para revisión de seguridad cargar la regla `seguridad.md`."
18
+ - "No cargar para implementaciones de auth en Rails (Devise) o Laravel (Sanctum/Passport) — las implementaciones aquí son para FastAPI/Django; para Rails cargar `rails-experto`."
19
+ evolvable: true # default para skill estandar
20
+ ---
21
+ # Autenticación y Autorización — Patrones de Seguridad
22
+
23
+ ## Cuándo NO cargar
24
+
25
+ - La tarea es seguridad de infraestructura: firewall, VPN, TLS, configuración de certificados — este skill cubre autenticación en la aplicación, no seguridad de red.
26
+ - El tema es gestión de secretos en cloud: AWS Secrets Manager, HashiCorp Vault, Azure Key Vault — para infraestructura de secretos cargar `cloud-infra`.
27
+ - La tarea es auditoría de seguridad de código o revisión OWASP Top 10 — cargar la regla `seguridad.md`.
28
+ - El framework es Rails (Devise) o Laravel (Sanctum/Passport) — las implementaciones aquí son para Python/FastAPI/Django; para Rails cargar `rails-experto`.
29
+
30
+ La autenticación ("¿quién eres?") y la autorización ("¿qué puedes hacer?") son las
31
+ capas de seguridad más críticas en cualquier sistema. Un error aquí compromete todo lo demás.
32
+
33
+ ---
34
+
35
+ ## 1. JWT — JSON Web Tokens
36
+
37
+ ```python
38
+ import os
39
+ from datetime import datetime, timedelta, timezone
40
+ from jose import JWTError, jwt
41
+
42
+ SECRET_KEY = os.environ["JWT_SECRET_KEY"] # NUNCA hardcodear — min 256 bits
43
+ ALGORITHM = "HS256"
44
+ ACCESS_TOKEN_EXPIRE_MINUTES = 30
45
+
46
+ def crear_access_token(payload: dict) -> str:
47
+ datos = payload.copy()
48
+ datos["exp"] = datetime.now(timezone.utc) + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
49
+ datos["iat"] = datetime.now(timezone.utc)
50
+ datos["type"] = "access"
51
+ return jwt.encode(datos, SECRET_KEY, algorithm=ALGORITHM)
52
+
53
+ def verificar_token(token: str, tipo_esperado: str = "access") -> dict:
54
+ try:
55
+ payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
56
+ except JWTError as e:
57
+ raise CredencialesInvalidas("Token inválido o expirado") from e
58
+ if payload.get("type") != tipo_esperado:
59
+ raise CredencialesInvalidas(f"Se esperaba token de tipo '{tipo_esperado}'")
60
+ return payload
61
+ ```
62
+
63
+ ### Claims OBLIGATORIOS: `sub`, `exp`, `iat`, `type`, `jti` (para revocación)
64
+
65
+ ---
66
+
67
+ ## 2. FastAPI — Auth Completo
68
+
69
+ ```python
70
+ from fastapi import Depends, HTTPException, status
71
+ from fastapi.security import OAuth2PasswordBearer
72
+
73
+ oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/token")
74
+
75
+ async def get_current_user(
76
+ token: str = Depends(oauth2_scheme),
77
+ db: AsyncSession = Depends(get_db),
78
+ ) -> Usuario:
79
+ credenciales_exc = HTTPException(status_code=401, detail="No autenticado")
80
+ try:
81
+ payload = verificar_token(token, tipo_esperado="access")
82
+ user_id = payload.get("sub", "")
83
+ if not user_id:
84
+ raise credenciales_exc
85
+ except CredencialesInvalidas:
86
+ raise credenciales_exc
87
+ usuario = await db.get(Usuario, user_id)
88
+ if usuario is None or not usuario.activo:
89
+ raise credenciales_exc
90
+ return usuario
91
+
92
+ def require_role(roles_permitidos: list[str]):
93
+ async def verificar_rol(usuario: Usuario = Depends(get_current_user)) -> Usuario:
94
+ if not {r.nombre for r in usuario.roles}.intersection(roles_permitidos):
95
+ raise HTTPException(status_code=403, detail="Sin permisos")
96
+ return usuario
97
+ return verificar_rol
98
+ ```
99
+
100
+ ---
101
+
102
+ ## 3. RBAC — Control de Acceso Basado en Roles
103
+
104
+ ```python
105
+ PERMISOS: dict[str, list[str]] = {
106
+ "empleados:crear": ["ADMIN", "RRHH"],
107
+ "empleados:editar": ["ADMIN", "RRHH"],
108
+ "empleados:ver": ["ADMIN", "RRHH", "NOMINA", "AUDITOR"],
109
+ "nomina:procesar": ["ADMIN", "NOMINA"],
110
+ }
111
+
112
+ # ABAC — combinar rol + contexto
113
+ def puede_ver_expediente(usuario: Usuario, expediente: Expediente) -> bool:
114
+ """Un empleado solo puede ver su propio expediente."""
115
+ if "ADMIN" in {r.nombre for r in usuario.roles}:
116
+ return True
117
+ if "RRHH" in {r.nombre for r in usuario.roles}:
118
+ return expediente.departamento_id == usuario.departamento_id
119
+ return expediente.empleado_id == usuario.id
120
+ ```
121
+
122
+ ---
123
+
124
+ ## 4. IDOR — Insecure Direct Object Reference
125
+
126
+ SIEMPRE verificar que el recurso pertenece al usuario autenticado:
127
+
128
+ ```python
129
+ # MAL: sin verificar propiedad
130
+ return await db.get(Expediente, id)
131
+
132
+ # BIEN: verificar autorización
133
+ expediente = await db.get(Expediente, id)
134
+ if not puede_ver_expediente(usuario, expediente):
135
+ raise HTTPException(403, "Sin autorización")
136
+ ```
137
+
138
+ ---
139
+
140
+ ## Implementaciones Completas
141
+
142
+ Para Refresh Token con rotación, OAuth2/OIDC con authlib, Passwords (bcrypt + validación),
143
+ MFA con TOTP (pyotp + QR), y Headers de seguridad HTTP, ver
144
+ [recursos/implementaciones-completas.md](recursos/implementaciones-completas.md).
145
+
146
+ ---
147
+
148
+ ## Gotchas / Errores comunes no obvios
149
+
150
+ **JWT con `alg: none` aceptado por librerías mal configuradas permite falsificar tokens sin firma**: algunas versiones de `python-jose` y `PyJWT` sin la opción `algorithms=` explícita aceptaban `alg: none` — cualquier payload sin firma era válido. Causa: el estándar JWT permite `alg: none` para tokens no firmados; las librerías que no restringen el algoritmo aceptan esto. Fix: SIEMPRE especificar `algorithms=["HS256"]` (o el algoritmo usado) al decodificar; NUNCA pasar `options={"verify_signature": False}` en producción.
151
+
152
+ **Refresh token almacenado en `localStorage` es vulnerable a XSS aunque el access token esté en httpOnly cookie**: si el refresh token está en `localStorage` y hay una vulnerabilidad XSS en la app, el atacante puede robar el refresh token y obtener nuevos access tokens indefinidamente. Causa: `localStorage` es accesible por JavaScript — cualquier script en la página puede leerlo. Fix: almacenar tanto el access token como el refresh token en cookies `httpOnly; Secure; SameSite=Strict`; usar un endpoint `/auth/refresh` que lea la cookie httpOnly y retorne un nuevo access token.
153
+
154
+ **`require_role` implementado como dependencia de FastAPI no se aplica a rutas OPTIONS (preflight CORS)**: el decorador de RBAC en FastAPI bloquea las peticiones OPTIONS del preflight si el middleware CORS no intercepta antes. Esto causa errores `403 Forbidden` en peticiones cross-origin aunque el usuario tenga el rol correcto. Causa: FastAPI ejecuta las dependencias antes de que el middleware CORS responda OPTIONS. Fix: configurar el middleware CORS de FastAPI con `CORSMiddleware` ANTES de cualquier middleware de auth, o excluir explícitamente el método `OPTIONS` de las verificaciones de auth.
155
+
156
+ **Tokens JWT con `exp` muy largo (7+ días) no se pueden invalidar después de un compromiso**: si un access token de 7 días es robado, el usuario no tiene forma de invalidarlo porque JWT es stateless — el servidor no puede verificar si fue revocado. Causa: el diseño stateless de JWT. Fix: mantener access tokens con expiración corta (15-30 min) y usar una allowlist/denylist de `jti` en Redis para tokens revocados; invalidar el `jti` al logout o al detectar actividad sospechosa.
157
+
158
+ ## Multitenancy: queries auth-aware desde el repositorio
159
+
160
+ La prevención de IDOR capa por capa (middleware + controlador + servicio) es
161
+ frágil — si una nueva ruta olvida el check, el bug pasa a producción
162
+ silenciosamente. **Patrón recomendado (observado en Polar y otros sistemas
163
+ SaaS multitenant): empujar el filtro de `auth_subject` al repositorio**, no
164
+ al servicio. El repositorio expone un método `get_readable_statement(auth_subject)`
165
+ que retorna un `Select` ya filtrado. Cualquier query de lectura parte de ese
166
+ statement base.
167
+
168
+ ```python
169
+ # repository.py — SQLAlchemy 2.0 async
170
+ class ResourceRepository:
171
+ model = Resource
172
+
173
+ def get_readable_statement(
174
+ self, auth_subject: AuthSubject[User | Organization]
175
+ ) -> Select[tuple[Resource]]:
176
+ statement = select(self.model)
177
+ if is_user(auth_subject):
178
+ statement = statement.where(
179
+ Resource.organization_id.in_(
180
+ select(UserOrganization.organization_id).where(
181
+ UserOrganization.user_id == auth_subject.subject.id
182
+ )
183
+ )
184
+ )
185
+ elif is_organization(auth_subject):
186
+ statement = statement.where(
187
+ Resource.organization_id == auth_subject.subject.id
188
+ )
189
+ return statement
190
+ ```
191
+
192
+ ```python
193
+ # service.py — nunca construye queries; parte del statement readable
194
+ async def get_resource(auth_subject, resource_id):
195
+ repo = ResourceRepository.from_session(session)
196
+ statement = repo.get_readable_statement(auth_subject).where(
197
+ Resource.id == resource_id
198
+ )
199
+ # Si no es accesible, retorna None (indistinguible de "no existe").
200
+ # Indistinguibilidad evita filtrar información sobre la existencia del recurso.
201
+ return await session.scalar(statement)
202
+ ```
203
+
204
+ **Ventajas sobre verificación en controlador:**
205
+ - Imposible olvidar el check — la query misma está filtrada.
206
+ - Un solo punto de cambio si la regla de acceso evoluciona.
207
+ - IDOR eliminado por diseño — acceder a recurso ajeno retorna `None`.
208
+
209
+ **Reglas obligatorias:**
210
+
211
+ 1. **NUNCA exponer queries directas al modelo desde servicios** — porque es
212
+ donde se pierde el contexto de `auth_subject` y aparecen bugs IDOR; todo
213
+ parte de `get_readable_statement`.
214
+ 2. **`get_readable_statement` recibe exactamente un `auth_subject`; no existe
215
+ overload "sin auth"** — porque un overload "admin sin filtro" se convierte
216
+ en la puerta trasera que usa el 80% del código. Para bypass legítimo, crear
217
+ un método explícito `get_statement_as_superuser()` auditable con grep.
218
+ 3. **El filtro del repositorio NO sustituye la verificación de scope** — un
219
+ token OAuth puede tener acceso a la organización pero no al scope
220
+ `resources:write`; el scope se verifica en el endpoint, el filtro en el
221
+ repositorio. Son capas complementarias.
222
+
223
+ **Refuerzo opcional con hook:** un PreToolUse que bloquee `select(Model)` en
224
+ archivos `service.py` fuerza el patrón. Ver `hooks/calidad-pre-commit.js` como
225
+ modelo de implementación.
226
+
227
+ Aplica a: FastAPI, Django (custom manager con `.for_user(user)`), Rails
228
+ (`scope :readable_by, ->(user) { ... }`), Laravel (Eloquent scopes globales
229
+ con `Model::booted`), cualquier ORM con query object pattern.
230
+
231
+ ## Checklist de Seguridad Auth
232
+
233
+ - [ ] Passwords hasheados con bcrypt (rounds >= 10)
234
+ - [ ] JWT con expiración corta (access <= 30 min)
235
+ - [ ] Refresh tokens rotados en cada uso
236
+ - [ ] Refresh tokens almacenados en BD (para revocación)
237
+ - [ ] Rate limiting en endpoints de login y refresh
238
+ - [ ] HTTPS obligatorio en producción
239
+ - [ ] Headers de seguridad configurados
240
+ - [ ] IDOR verificado en TODOS los endpoints con recursos por ID
241
+ - [ ] RBAC en todos los endpoints de escritura
242
+ - [ ] Logs de auditoría en login, logout, cambio de contraseña
243
+ - [ ] Secrets en variables de entorno, nunca en código
244
+ - [ ] MFA disponible para roles privilegiados (ADMIN, NOMINA)
245
+ - [ ] Tokens no almacenados en localStorage (usar httpOnly cookies)
246
+
247
+ ---
248
+
249
+ ## IDOR defense: responder 404 en lugar de 403 cuando el recurso ajeno existe
250
+
251
+ ### Regla
252
+
253
+ Cuando un usuario autenticado pero **no-dueño** intenta acceder a un recurso
254
+ ajeno identificable por ID (IDOR, Insecure Direct Object Reference):
255
+
256
+ - Responder **404 Not Found** — idéntico al caso donde el recurso no existe.
257
+ - **Nunca** responder 403 Forbidden distinguiendo "existe pero no puedes verlo".
258
+
259
+ ### Por qué
260
+
261
+ `403` revela la existencia del recurso a terceros autenticados. Un atacante
262
+ puede enumerar IDs secuenciales (`/pedidos/1000`, `/pedidos/1001`, ...) y
263
+ distinguir:
264
+
265
+ - **404** → el recurso no existe
266
+ - **403** → el recurso existe pero es de otro usuario (fuga de información)
267
+
268
+ Con 404 uniforme, el atacante no puede mapear el universo de IDs existentes.
269
+ El dueño legítimo del recurso obtiene 200; todos los demás obtienen 404 con
270
+ el mismo body genérico.
271
+
272
+ ### Patrón canónico (FastAPI)
273
+
274
+ ```python
275
+ from fastapi import HTTPException, status, Depends
276
+
277
+ async def _verificar_ownership(
278
+ recurso_id: int,
279
+ user: Usuario = Depends(get_current_user),
280
+ db: AsyncSession = Depends(get_db),
281
+ ) -> Recurso:
282
+ """Retorna el recurso solo si existe Y el user es su dueño.
283
+
284
+ Caso no-dueño → 404 idéntico al caso de ID inexistente.
285
+ """
286
+ stmt = select(Recurso).where(
287
+ Recurso.id == recurso_id,
288
+ Recurso.usuario_id == user.id, # filtro authoritativo server-side
289
+ )
290
+ recurso = (await db.execute(stmt)).scalar_one_or_none()
291
+
292
+ if recurso is None:
293
+ # No distinguir "no existe" de "no es tuyo"
294
+ raise HTTPException(
295
+ status_code=status.HTTP_404_NOT_FOUND,
296
+ detail="Recurso no encontrado",
297
+ )
298
+ return recurso
299
+
300
+
301
+ @router.get("/pedidos/{pedido_id}")
302
+ async def obtener_pedido(
303
+ pedido: Pedido = Depends(_verificar_ownership),
304
+ ):
305
+ return pedido
306
+ ```
307
+
308
+ ### Anti-patrón
309
+
310
+ ```python
311
+ # MAL — fuga de información
312
+ pedido = await db.get(Pedido, pedido_id)
313
+ if pedido is None:
314
+ raise HTTPException(404)
315
+ if pedido.usuario_id != user.id:
316
+ raise HTTPException(403, "No tienes permiso para ver este pedido") # ← revela existencia
317
+ return pedido
318
+ ```
319
+
320
+ ### Excepciones controladas
321
+
322
+ - Si el recurso es **compartido por diseño** (archivo público, post de foro),
323
+ el control de acceso no aplica y la respuesta puede ser 200 legítimamente.
324
+ - En admin panels donde el usuario **debe** saber qué IDs existen para
325
+ moderar, el rol admin puede recibir 403 con detalle; usuarios normales 404.
326
+ - Logs del servidor deben distinguir ambos casos para detección de abuso
327
+ (un mismo usuario acumulando 404s sobre IDs secuenciales es señal de
328
+ enumeración).
329
+
330
+ ### Frameworks de seguridad
331
+
332
+ - OWASP Top 10 A01:2021 — Broken Access Control.
333
+ - NIST CSF PR.AA-05 — Access rights and permissions are managed.
334
+ - MITRE ATT&CK T1613 — Container and Resource Discovery.
335
+
336
+ ---
337
+
338
+ ## Consent PII obligatorio en TODOS los ambientes (dev/staging/prod)
339
+
340
+ ### Regla
341
+
342
+ Toda feature que procese datos personales identificables (PII) requiere consent
343
+ explícito del titular. El gate de consent **debe activarse en TODOS los
344
+ ambientes**, no solo en producción.
345
+
346
+ ### Por qué
347
+
348
+ Las regulaciones de protección de datos (GDPR Art. 6, CCPA, LGPD Brasil,
349
+ LGPDPPSO México) **no distinguen entre ambientes**. Un bug en staging que
350
+ transfiere PII a un LLM externo sin consent es la misma violación que en
351
+ producción, con el agravante de que:
352
+
353
+ - Los datos de staging suelen ser "datos reales de hace 30 días" que siguen
354
+ siendo PII.
355
+ - El pipeline de LLM en staging puede loggear prompts en el proveedor (OpenAI,
356
+ Anthropic) que ya es "transferencia a terceros".
357
+ - El mismo bug detectado en staging se deploya a prod sin fix si el gate no
358
+ se probó activo en staging.
359
+
360
+ ### Anti-patrón más común
361
+
362
+ ```python
363
+ # MAL — gate solo en production deja fugas en dev/staging
364
+ def requiere_consent_transferencia_pii(user: Usuario) -> bool:
365
+ if settings.environment != "production":
366
+ return False # ← aquí está el agujero
367
+ return not user.consent_transferencia_internacional
368
+ ```
369
+
370
+ ### Patrón canónico
371
+
372
+ ```python
373
+ # BIEN — consent obligatorio en todos los ambientes
374
+ def requiere_consent_transferencia_pii(user: Usuario) -> bool:
375
+ """Gate activo independiente del ambiente.
376
+
377
+ Los ambientes dev/staging suelen tener datos clonados de producción
378
+ o sintéticos-pero-realistas. Procesarlos sin consent viola la misma
379
+ regulación que producción.
380
+ """
381
+ if not user.consent_transferencia_internacional:
382
+ return True
383
+
384
+ # Verificación adicional: consent no expirado
385
+ if user.consent_fecha + CONSENT_VIGENCIA < datetime.now(UTC):
386
+ return True
387
+
388
+ return False
389
+
390
+
391
+ # En el resolver/factory del cliente LLM:
392
+ def resolver_llm_client(user: Usuario):
393
+ if requiere_consent_transferencia_pii(user):
394
+ # Usar solo LLM on-premise o rechazar la operación
395
+ return _cliente_local_sin_egreso()
396
+ return _cliente_cloud_con_consent_vigente()
397
+ ```
398
+
399
+ ### Checklist de consent
400
+
401
+ - [ ] El modelo `Usuario` tiene campo `consent_transferencia_internacional` con default `False`.
402
+ - [ ] El consent se registra con timestamp y versión del texto legal aceptado (auditable).
403
+ - [ ] El consent expira (típicamente 12-24 meses) y requiere re-confirmación.
404
+ - [ ] El gate se ejecuta en dev/staging/prod sin flag de bypass por ambiente.
405
+ - [ ] Hay test de integración que valida que un user sin consent recibe error en staging (no solo en prod).
406
+ - [ ] El log de decisión del gate incluye `user_id`, `decision`, `ambiente`, `motivo` para auditoría.
407
+ - [ ] Existe endpoint de revocación con efecto inmediato (invalida cualquier transferencia en curso).
408
+
409
+ ### Frameworks de seguridad
410
+
411
+ - GDPR Art. 6 (bases de licitud), Art. 44-50 (transferencias internacionales).
412
+ - NIST CSF GV.OC-03 — Legal, regulatory, and contractual requirements regarding cybersecurity are understood and managed.
413
+ - NIST AI RMF GOVERN-6.1 — Policies and procedures are in place to address AI risks.
@@ -0,0 +1,229 @@
1
+ # Autenticación y Autorización — Implementaciones Completas
2
+
3
+ ## Refresh Token — Rotación segura
4
+
5
+ ```python
6
+ # Tabla de refresh tokens en BD (para invalidación)
7
+ class RefreshToken(Base):
8
+ __tablename__ = "refresh_tokens"
9
+ id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
10
+ usuario_id = Column(UUID(as_uuid=True), ForeignKey("usuarios.id"), nullable=False)
11
+ token_hash = Column(String(64), nullable=False, unique=True) # SHA-256
12
+ expires_at = Column(DateTime(timezone=True), nullable=False)
13
+ revocado_en = Column(DateTime(timezone=True), nullable=True)
14
+ ip_address = Column(String(45), nullable=True)
15
+
16
+ import hashlib
17
+
18
+ @router.post("/auth/refresh", response_model=TokenResponse)
19
+ async def refresh_token_endpoint(
20
+ body: RefreshRequest,
21
+ db: AsyncSession = Depends(get_db),
22
+ request: Request = None,
23
+ ) -> TokenResponse:
24
+ try:
25
+ payload = verificar_token(body.refresh_token, tipo_esperado="refresh")
26
+ except CredencialesInvalidas:
27
+ raise HTTPException(status_code=401, detail="Refresh token inválido")
28
+
29
+ # Verificar contra BD (detección de reuso de tokens robados)
30
+ token_hash = hashlib.sha256(body.refresh_token.encode()).hexdigest()
31
+ rt = await db.execute(
32
+ select(RefreshToken).where(
33
+ RefreshToken.token_hash == token_hash,
34
+ RefreshToken.revocado_en.is_(None),
35
+ RefreshToken.expires_at > datetime.now(timezone.utc),
36
+ )
37
+ )
38
+ rt = rt.scalar_one_or_none()
39
+ if rt is None:
40
+ # Posible reuso — revocar TODOS los tokens del usuario (token family)
41
+ await revocar_todos_tokens_usuario(db, payload["sub"])
42
+ raise HTTPException(status_code=401, detail="Refresh token inválido o reusado")
43
+
44
+ # Rotar: revocar el anterior, emitir uno nuevo
45
+ rt.revocado_en = datetime.now(timezone.utc)
46
+ nuevo_refresh = crear_refresh_token(payload["sub"])
47
+ db.add(RefreshToken(
48
+ usuario_id=payload["sub"],
49
+ token_hash=hashlib.sha256(nuevo_refresh.encode()).hexdigest(),
50
+ expires_at=datetime.now(timezone.utc) + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS),
51
+ ip_address=request.client.host if request else None,
52
+ ))
53
+ await db.commit()
54
+
55
+ usuario = await db.get(Usuario, payload["sub"])
56
+ return TokenResponse(
57
+ access_token=crear_access_token({"sub": str(usuario.id)}),
58
+ refresh_token=nuevo_refresh,
59
+ token_type="bearer",
60
+ )
61
+ ```
62
+
63
+ ---
64
+
65
+ ## OAuth2 / OIDC — Integración con proveedores externos
66
+
67
+ ```python
68
+ # Con authlib — pip install authlib
69
+ from authlib.integrations.starlette_client import OAuth
70
+ from starlette.config import Config
71
+
72
+ config = Config(".env")
73
+ oauth = OAuth(config)
74
+
75
+ # Registro de proveedor (ej: Azure AD / Microsoft Entra)
76
+ oauth.register(
77
+ name="microsoft",
78
+ client_id=os.environ["AZURE_CLIENT_ID"],
79
+ client_secret=os.environ["AZURE_CLIENT_SECRET"],
80
+ server_metadata_url=(
81
+ f"https://login.microsoftonline.com/{os.environ['AZURE_TENANT_ID']}"
82
+ "/v2.0/.well-known/openid-configuration"
83
+ ),
84
+ client_kwargs={"scope": "openid email profile"},
85
+ )
86
+
87
+ @router.get("/auth/microsoft/login")
88
+ async def login_microsoft(request: Request):
89
+ redirect_uri = str(request.url_for("auth_microsoft_callback"))
90
+ return await oauth.microsoft.authorize_redirect(request, redirect_uri)
91
+
92
+ @router.get("/auth/microsoft/callback")
93
+ async def auth_microsoft_callback(request: Request, db: AsyncSession = Depends(get_db)):
94
+ try:
95
+ token = await oauth.microsoft.authorize_access_token(request)
96
+ except Exception as e:
97
+ raise HTTPException(status_code=400, detail="Autenticación fallida")
98
+
99
+ userinfo = token.get("userinfo") or await oauth.microsoft.userinfo(token=token)
100
+ email = userinfo["email"]
101
+
102
+ usuario = await obtener_o_crear_usuario_oauth(db, email, userinfo)
103
+ await db.commit()
104
+
105
+ access_token = crear_access_token({"sub": str(usuario.id), "email": email})
106
+ return RedirectResponse(
107
+ f"{os.environ['FRONTEND_URL']}/auth/callback?token={access_token}"
108
+ )
109
+ ```
110
+
111
+ ---
112
+
113
+ ## Passwords — Hashing y validación de fortaleza
114
+
115
+ ```python
116
+ from passlib.context import CryptContext
117
+
118
+ pwd_context = CryptContext(
119
+ schemes=["bcrypt"],
120
+ deprecated="auto",
121
+ bcrypt__rounds=12,
122
+ )
123
+
124
+ def hash_password(password: str) -> str:
125
+ return pwd_context.hash(password)
126
+
127
+ def verificar_password(plain: str, hashed: str) -> bool:
128
+ return pwd_context.verify(plain, hashed)
129
+
130
+ # Validación de fortaleza de password
131
+ import re
132
+
133
+ def validar_fortaleza_password(password: str) -> list[str]:
134
+ """Retorna lista de errores. Lista vacía = password válido."""
135
+ errores = []
136
+ if len(password) < 12:
137
+ errores.append("Mínimo 12 caracteres")
138
+ if not re.search(r"[A-Z]", password):
139
+ errores.append("Al menos una mayúscula")
140
+ if not re.search(r"[a-z]", password):
141
+ errores.append("Al menos una minúscula")
142
+ if not re.search(r"\d", password):
143
+ errores.append("Al menos un número")
144
+ if not re.search(r"[!@#$%^&*()_+\-=\[\]{}|;:,.<>?]", password):
145
+ errores.append("Al menos un carácter especial")
146
+ return errores
147
+ ```
148
+
149
+ ---
150
+
151
+ ## MFA — Autenticación Multifactor (TOTP)
152
+
153
+ ```python
154
+ # pip install pyotp qrcode
155
+ import pyotp
156
+ import qrcode
157
+ from io import BytesIO
158
+ import base64
159
+
160
+ def generar_secreto_totp() -> str:
161
+ return pyotp.random_base32()
162
+
163
+ def generar_qr_totp(email: str, secreto: str, emisor: str = "MiEmpresa") -> str:
164
+ """Genera imagen QR en base64 para escanear con Google Authenticator."""
165
+ totp = pyotp.TOTP(secreto)
166
+ uri = totp.provisioning_uri(name=email, issuer_name=emisor)
167
+
168
+ qr = qrcode.make(uri)
169
+ buffer = BytesIO()
170
+ qr.save(buffer, format="PNG")
171
+ return base64.b64encode(buffer.getvalue()).decode()
172
+
173
+ def verificar_totp(secreto: str, codigo: str) -> bool:
174
+ """Verifica un código TOTP. Permite ventana de +/-1 intervalo (30s)."""
175
+ totp = pyotp.TOTP(secreto)
176
+ return totp.verify(codigo, valid_window=1)
177
+
178
+ # Endpoint de activación MFA
179
+ @router.post("/auth/mfa/activar")
180
+ async def activar_mfa(
181
+ usuario: Usuario = Depends(get_current_user),
182
+ db: AsyncSession = Depends(get_db),
183
+ ):
184
+ secreto = generar_secreto_totp()
185
+ usuario.mfa_secreto_pendiente = secreto
186
+ await db.commit()
187
+ return {
188
+ "secreto": secreto,
189
+ "qr_code": generar_qr_totp(usuario.email, secreto),
190
+ }
191
+
192
+ @router.post("/auth/mfa/confirmar")
193
+ async def confirmar_mfa(
194
+ body: MFAConfirmRequest,
195
+ usuario: Usuario = Depends(get_current_user),
196
+ db: AsyncSession = Depends(get_db),
197
+ ):
198
+ if not usuario.mfa_secreto_pendiente:
199
+ raise HTTPException(400, "No hay MFA pendiente de activar")
200
+ if not verificar_totp(usuario.mfa_secreto_pendiente, body.codigo):
201
+ raise HTTPException(400, "Código TOTP inválido")
202
+ usuario.mfa_secreto = usuario.mfa_secreto_pendiente
203
+ usuario.mfa_secreto_pendiente = None
204
+ usuario.mfa_activo = True
205
+ await db.commit()
206
+ return {"mensaje": "MFA activado correctamente"}
207
+ ```
208
+
209
+ ---
210
+
211
+ ## Headers de seguridad
212
+
213
+ ```python
214
+ from fastapi.middleware.trustedhost import TrustedHostMiddleware
215
+ from starlette.middleware.httpsredirect import HTTPSRedirectMiddleware
216
+
217
+ app.add_middleware(HTTPSRedirectMiddleware)
218
+ app.add_middleware(TrustedHostMiddleware, allowed_hosts=["miapp.com.mx", "*.miapp.com.mx"])
219
+
220
+ @app.middleware("http")
221
+ async def security_headers(request, call_next):
222
+ response = await call_next(request)
223
+ response.headers["X-Content-Type-Options"] = "nosniff"
224
+ response.headers["X-Frame-Options"] = "DENY"
225
+ response.headers["X-XSS-Protection"] = "1; mode=block"
226
+ response.headers["Strict-Transport-Security"] = "max-age=31536000; includeSubDomains"
227
+ response.headers["Content-Security-Policy"] = "default-src 'self'"
228
+ return response
229
+ ```