<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Gabriela Estefania Cohaila Alvarado</title>
    <description>The latest articles on DEV Community by Gabriela Estefania Cohaila Alvarado (@gabrielacohailaalvaradoe).</description>
    <link>https://dev.to/gabrielacohailaalvaradoe</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4160388%2F08b64c23-5c52-44bc-8aa0-fd2c97f19448.jpg</url>
      <title>DEV Community: Gabriela Estefania Cohaila Alvarado</title>
      <link>https://dev.to/gabrielacohailaalvaradoe</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/gabrielacohailaalvaradoe"/>
    <language>en</language>
    <item>
      <title>Diseño e implementación de una arquitectura web para una red social universitaria</title>
      <dc:creator>Gabriela Estefania Cohaila Alvarado</dc:creator>
      <pubDate>Mon, 05 Oct 2026 02:33:48 +0000</pubDate>
      <link>https://dev.to/gabrielacohailaalvaradoe/diseno-e-implementacion-de-una-arquitectura-web-para-una-red-social-universitaria-2e6g</link>
      <guid>https://dev.to/gabrielacohailaalvaradoe/diseno-e-implementacion-de-una-arquitectura-web-para-una-red-social-universitaria-2e6g</guid>
      <description>&lt;p&gt;&lt;strong&gt;Autora:&lt;/strong&gt; Gabriela Estefania Cohaila Alvarado&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Código:&lt;/strong&gt; 2022075746&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Proyecto:&lt;/strong&gt; CampusConecta  &lt;/p&gt;

&lt;h2&gt;
  
  
  Resumen
&lt;/h2&gt;

&lt;p&gt;Este artículo describe el diseño y la implementación de CampusConecta, una plataforma web para la interacción entre estudiantes, docentes y personal universitario. La solución se construyó con React y TypeScript en el cliente, ASP.NET Core Web API en el servidor y PostgreSQL como base de datos. El trabajo se enfoca en las decisiones de arquitectura, el modelo relacional, la API REST, la autenticación mediante JSON Web Token, la validación de datos y la persistencia. La verificación incluyó seis pruebas automáticas de integración, compilación del frontend y un flujo HTTP ejecutado contra PostgreSQL real. En ese flujo se creó una publicación, un comentario y una reacción; posteriormente, la búsqueda y el panel personal recuperaron los cambios persistidos. Las seis pruebas automáticas aprobaron. Una muestra exploratoria de veinte consultas locales al feed obtuvo una media de 5.40 ms y un percentil 95 de 9.58 ms. Estas cifras corresponden al entorno de desarrollo y no constituyen una prueba de carga. El resultado es una plataforma funcional, reproducible y preparada para evolucionar hacia un escenario institucional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Palabras clave:&lt;/strong&gt; arquitectura web, ASP.NET Core, API REST, JWT, PostgreSQL, React, red social universitaria.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducción
&lt;/h2&gt;

&lt;p&gt;Un sitio de red social permite construir perfiles, relacionar participantes e intercambiar contenido dentro de un sistema delimitado. Boyd y Ellison definen estos elementos como rasgos centrales de las redes sociales en línea [1]. En una universidad, la misma idea requiere adaptar la interacción a una comunidad con estudiantes, docentes y personal, así como proteger las operaciones que cambian contenido o identidad.&lt;/p&gt;

&lt;p&gt;CampusConecta se desarrolló como una aplicación web completa y ejecutable. No se planteó como una interfaz estática. Cada interacción visible se conecta con una ruta de la API y con una operación persistente. El sistema cubre registro, inicio de sesión, perfiles, publicaciones, comentarios, reacciones, comunidades, membresías, búsqueda y un panel personal. También incorpora respuestas de error y documentación Swagger para inspeccionar los contratos HTTP.&lt;/p&gt;

&lt;p&gt;El reto principal consistió en integrar estas funciones sin concentrar la lógica en la interfaz ni permitir que el cliente decidiera permisos sensibles. Por ello, el proyecto separa presentación, servicios y persistencia. Esta organización facilita probar el backend de forma aislada, usar otro cliente en el futuro y mantener las reglas de autorización cerca de los datos que protegen.&lt;/p&gt;

&lt;h2&gt;
  
  
  Objetivo
&lt;/h2&gt;

&lt;p&gt;Diseñar e implementar una arquitectura web para una red social universitaria que incluya API REST, autenticación JWT, persistencia PostgreSQL, interfaz React y mecanismos básicos de validación y manejo de errores.&lt;/p&gt;

&lt;p&gt;El alcance implementado incluye:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Registro e inicio de sesión.&lt;/li&gt;
&lt;li&gt;Consulta y edición del perfil propio.&lt;/li&gt;
&lt;li&gt;Feed, creación y eliminación autorizada de publicaciones.&lt;/li&gt;
&lt;li&gt;Comentarios y reacciones.&lt;/li&gt;
&lt;li&gt;Creación, consulta y administración básica de comunidades.&lt;/li&gt;
&lt;li&gt;Unión y salida de comunidades.&lt;/li&gt;
&lt;li&gt;Búsqueda de usuarios, publicaciones y comunidades.&lt;/li&gt;
&lt;li&gt;Panel personal con métricas y actividad reciente.&lt;/li&gt;
&lt;li&gt;Swagger/OpenAPI, endpoint de salud, migraciones y pruebas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No se implementaron mensajería privada, recuperación de contraseñas, carga de archivos, notificaciones en tiempo real ni autenticación federada. Declarar estos límites evita atribuir al sistema capacidades que no fueron desarrolladas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Arquitectura de la solución
&lt;/h2&gt;

&lt;p&gt;CampusConecta sigue una arquitectura de tres capas desplegables.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Capa&lt;/th&gt;
&lt;th&gt;Tecnología&lt;/th&gt;
&lt;th&gt;Responsabilidad&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Presentación&lt;/td&gt;
&lt;td&gt;React 19, TypeScript y Vite&lt;/td&gt;
&lt;td&gt;Formularios, navegación, estados de carga, visualización del feed y mensajes al usuario.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Servicios&lt;/td&gt;
&lt;td&gt;ASP.NET Core 8 Web API&lt;/td&gt;
&lt;td&gt;Endpoints REST, contratos, validaciones, autenticación, autorización y manejo de errores.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Persistencia&lt;/td&gt;
&lt;td&gt;Entity Framework Core 8, Npgsql y PostgreSQL&lt;/td&gt;
&lt;td&gt;Modelo relacional, migraciones, consultas e integridad de los datos.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operación&lt;/td&gt;
&lt;td&gt;Swagger/OpenAPI y Docker Compose&lt;/td&gt;
&lt;td&gt;Exploración de la API, ejecución local y base de datos reproducible.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La aplicación React consume respuestas JSON a través de una capa de acceso centralizada. Esa capa adjunta el token Bearer a las solicitudes autenticadas y traduce los errores de la API en mensajes que el usuario puede comprender. La interfaz contiene vistas para acceso, feed, comunidades, búsqueda, panel y perfil.&lt;/p&gt;

&lt;p&gt;La API agrupa responsabilidades por recurso. &lt;code&gt;AuthController&lt;/code&gt; registra y autentica cuentas. &lt;code&gt;UsersController&lt;/code&gt; consulta y actualiza perfiles. &lt;code&gt;PostsController&lt;/code&gt; resuelve feed, publicaciones, comentarios y reacciones. &lt;code&gt;CommunitiesController&lt;/code&gt; administra comunidades y membresías. &lt;code&gt;SearchController&lt;/code&gt; combina resultados de diferentes recursos, y &lt;code&gt;DashboardController&lt;/code&gt; calcula los indicadores personales. Un middleware global transforma excepciones conocidas en respuestas HTTP consistentes y evita devolver trazas internas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Modelo de datos y persistencia
&lt;/h2&gt;

&lt;p&gt;El esquema relacional contiene seis entidades principales.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entidad&lt;/th&gt;
&lt;th&gt;Propósito y restricciones relevantes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Users&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Conserva identidad, rol, perfil y hash de contraseña. El correo y el nombre de usuario son únicos.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Communities&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Representa cada comunidad, su descripción, creador y estado.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;CommunityMembers&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Materializa la relación entre usuarios y comunidades; evita duplicar la membresía.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Posts&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Guarda contenido, fecha, autor y comunidad opcional.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Comments&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Relaciona un comentario con una publicación y su autor.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;Reactions&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Registra una reacción por combinación de usuario y publicación.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Las claves foráneas garantizan que una publicación no exista sin autor y que un comentario no exista sin publicación. Las restricciones únicas protegen correos, nombres de usuario, nombres de comunidad, membresías y reacciones repetidas. PostgreSQL documenta las restricciones como reglas que rechazan escrituras incompatibles con el esquema [2]. En CampusConecta complementan las validaciones de la API, no las sustituyen.&lt;/p&gt;

&lt;p&gt;Entity Framework Core genera y aplica la migración &lt;code&gt;InitialCreate&lt;/code&gt;. Durante la validación se usó una instancia PostgreSQL real. La migración se aplicó correctamente y, tras el flujo de aceptación, se verificaron 3 usuarios, 3 publicaciones, 2 comentarios, 3 reacciones, 1 comunidad y 3 membresías. La configuración de entrega incorpora Docker Compose con PostgreSQL 16 para que el entorno pueda reconstruirse sin depender de la instancia usada durante las pruebas.&lt;/p&gt;

&lt;h2&gt;
  
  
  Diseño de la API REST
&lt;/h2&gt;

&lt;p&gt;Las rutas describen recursos y utilizan verbos HTTP de acuerdo con la operación. &lt;code&gt;POST&lt;/code&gt; registra recursos; &lt;code&gt;GET&lt;/code&gt; consulta; &lt;code&gt;PUT&lt;/code&gt; actualiza perfil o reacción; y &lt;code&gt;DELETE&lt;/code&gt; elimina contenido propio o abandona una comunidad. El contrato se expone en Swagger, que permite examinar esquemas y probar endpoints durante el desarrollo. La especificación OpenAPI define un formato estándar para describir este tipo de interfaces [3].&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Área&lt;/th&gt;
&lt;th&gt;Operaciones representativas&lt;/th&gt;
&lt;th&gt;Protección&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Autenticación&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;POST /api/auth/register&lt;/code&gt;, &lt;code&gt;POST /api/auth/login&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Pública&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Perfil&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GET /api/users/me&lt;/code&gt;, &lt;code&gt;PUT /api/users/me&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;JWT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Publicaciones&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GET/POST /api/posts&lt;/code&gt;, comentarios y reacciones&lt;/td&gt;
&lt;td&gt;JWT; propiedad para eliminar&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Comunidades&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GET/POST /api/communities&lt;/code&gt;, &lt;code&gt;join&lt;/code&gt;, &lt;code&gt;leave&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;JWT; reglas de membresía&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Consulta&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GET /api/search&lt;/code&gt;, &lt;code&gt;GET /api/dashboard&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;JWT&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Operación&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;GET /health&lt;/code&gt;, &lt;code&gt;/swagger&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Pública en desarrollo&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;La API usa códigos de estado para distinguir condiciones. Una entrada inválida devuelve 400; una solicitud sin sesión devuelve 401; una acción sobre un recurso ajeno devuelve 403; y un recurso inexistente devuelve 404. Este comportamiento permite que el frontend informe el problema sin interpretar mensajes técnicos del servidor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Autenticación y autorización
&lt;/h2&gt;

&lt;p&gt;El registro valida el formato del correo, la longitud de los campos y la fortaleza mínima de la contraseña. La contraseña no se persiste como texto legible. ASP.NET Core &lt;code&gt;PasswordHasher&amp;lt;AppUser&amp;gt;&lt;/code&gt; crea un hash con salt y parámetros de trabajo. OWASP recomienda almacenar contraseñas con funciones lentas y adaptativas, y nunca en texto plano [4].&lt;/p&gt;

&lt;p&gt;Después de un inicio de sesión válido, la API genera un JWT firmado con identificador, correo, nombre de usuario y rol. RFC 7519 describe JWT como una representación compacta de claims intercambiable entre partes [5]. El middleware valida la firma, el emisor, la audiencia y el vencimiento antes de crear la identidad de la solicitud. Los controladores obtienen el identificador desde los claims y verifican propiedad o membresía antes de modificar un recurso.&lt;/p&gt;

&lt;p&gt;La prueba automática de rutas protegidas solicitó el panel personal sin token y recibió 401. Las operaciones de eliminación también se restringen en servidor. Por ejemplo, ocultar un botón en React no basta: la API revisa que el autor de la solicitud sea el propietario del contenido.&lt;/p&gt;

&lt;p&gt;La clave JWT y las credenciales de base de datos incluidas en la configuración son únicamente valores de desarrollo local. Antes de desplegar se deben reemplazar mediante variables de entorno o un gestor de secretos, habilitar HTTPS y reducir la exposición de Swagger. Para una universidad, la siguiente mejora prioritaria es incorporar OpenID Connect con la identidad institucional. Microsoft indica que los tokens propios son apropiados para sistemas cerrados y pruebas, pero no reemplazan una autoridad de identidad administrada [6].&lt;/p&gt;

&lt;h2&gt;
  
  
  Implementación del frontend
&lt;/h2&gt;

&lt;p&gt;La interfaz se construyó con React, TypeScript y Vite. React organiza la experiencia mediante componentes que combinan estado y representación [7]. En CampusConecta, la vista principal recupera el feed y permite crear publicaciones; cada tarjeta muestra autor, fecha, comunidad, reacciones y comentarios. Las vistas de comunidad indican miembros y estado de unión. La búsqueda muestra resultados por tipo y el panel personal resume actividad reciente.&lt;/p&gt;

&lt;p&gt;Los formularios controlan datos incompletos antes de enviar la solicitud, pero la validación definitiva está en la API. Las respuestas de error se muestran como mensajes de interfaz. Esta combinación evita solicitudes triviales innecesarias sin confiar en el navegador para decisiones de seguridad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verificación técnica
&lt;/h2&gt;

&lt;p&gt;La verificación combinó pruebas automáticas, revisión del frontend y ejecución integral.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Verificación&lt;/th&gt;
&lt;th&gt;Resultado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;dotnet test CampusConecta.sln --configuration Release&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;6 pruebas aprobadas, 0 fallidas, 0 omitidas.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run lint&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Aprobado sin advertencias.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;npm run build&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Build de producción correcto.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;PostgreSQL real&lt;/td&gt;
&lt;td&gt;Migración aplicada y flujo de escritura/lectura confirmado.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Interfaz&lt;/td&gt;
&lt;td&gt;Ocho capturas reales de los recorridos principales.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Las pruebas de API cubrieron salud, hash de una cuenta inicial, rechazo de contraseña débil, registro con JWT, feed, publicaciones, comentarios, reacciones, panel, comunidades, búsqueda, perfil y rechazo de acceso sin JWT. La prueba integral creó una publicación, un comentario y una reacción mediante HTTP; la búsqueda recuperó la publicación y el panel actualizó sus indicadores.&lt;/p&gt;

&lt;p&gt;Se realizaron veinte solicitudes consecutivas al feed en el mismo equipo que alojaba la API. La media fue 5.40 ms, la mediana 4.73 ms, el percentil 95 9.58 ms, el mínimo 3.27 ms y el máximo 11.21 ms. La muestra permite detectar regresiones locales; no mide concurrencia, red pública ni capacidad productiva.&lt;/p&gt;

&lt;h2&gt;
  
  
  Discusión
&lt;/h2&gt;

&lt;p&gt;La arquitectura logró que cada requisito tuviera una representación visible, un endpoint y una regla de persistencia. Separar React de ASP.NET Core permitió validar el backend sin navegador y después confirmar la integración completa. PostgreSQL preservó las relaciones entre usuarios, contenido y comunidades mediante restricciones explícitas.&lt;/p&gt;

&lt;p&gt;Persisten límites propios del alcance académico. El feed no usa caché ni cursores; la búsqueda no incorpora índices especializados; las imágenes se manejan como URL y no existe almacenamiento administrado. Tampoco se realizaron pruebas de carga, auditoría externa de seguridad o evaluación con usuarios. Estas limitaciones son puntos de evolución, no resultados omitidos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusiones
&lt;/h2&gt;

&lt;p&gt;CampusConecta demuestra una implementación funcional de red social universitaria con arquitectura separada, API REST, JWT, PostgreSQL, migraciones y Swagger. Las acciones principales modificaron una base real y sus resultados aparecieron en feed, búsqueda y panel. Las seis pruebas aprobadas y la compilación del frontend aportan evidencia de corrección en el alcance desarrollado.&lt;/p&gt;

&lt;p&gt;La siguiente etapa debe incorporar autenticación institucional, secretos externos, HTTPS obligatorio, auditoría de acciones, reportes de contenido, pruebas de carga y accesibilidad asistida. Estas mejoras pueden añadirse sobre la estructura actual sin reescribir las capas principales.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;boyd, d. m., y Ellison, N. B. (2007). &lt;em&gt;Social Network Sites: Definition, History, and Scholarship&lt;/em&gt;. Journal of Computer-Mediated Communication, 13(1), 210–230. &lt;a href="https://doi.org/10.1111/j.1083-6101.2007.00393.x" rel="noopener noreferrer"&gt;https://doi.org/10.1111/j.1083-6101.2007.00393.x&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;PostgreSQL Global Development Group. (2026). &lt;em&gt;PostgreSQL documentation: Constraints&lt;/em&gt;. &lt;a href="https://www.postgresql.org/docs/current/ddl-constraints.html" rel="noopener noreferrer"&gt;https://www.postgresql.org/docs/current/ddl-constraints.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OpenAPI Initiative. (2026). &lt;em&gt;OpenAPI Specification&lt;/em&gt;. &lt;a href="https://spec.openapis.org/oas/" rel="noopener noreferrer"&gt;https://spec.openapis.org/oas/&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;OWASP Foundation. (2026). &lt;em&gt;Password Storage Cheat Sheet&lt;/em&gt;. &lt;a href="https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html" rel="noopener noreferrer"&gt;https://cheatsheetseries.owasp.org/cheatsheets/Password_Storage_Cheat_Sheet.html&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Jones, M., Bradley, J., y Sakimura, N. (2015). &lt;em&gt;JSON Web Token (JWT), RFC 7519&lt;/em&gt;. Internet Engineering Task Force. &lt;a href="https://www.rfc-editor.org/rfc/rfc7519" rel="noopener noreferrer"&gt;https://www.rfc-editor.org/rfc/rfc7519&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Microsoft. (2026). &lt;em&gt;Configure JWT bearer authentication in ASP.NET Core&lt;/em&gt;. &lt;a href="https://learn.microsoft.com/aspnet/core/security/authentication/configure-jwt-bearer-authentication" rel="noopener noreferrer"&gt;https://learn.microsoft.com/aspnet/core/security/authentication/configure-jwt-bearer-authentication&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;React Team. (2026). &lt;em&gt;React documentation&lt;/em&gt;. &lt;a href="https://react.dev/learn" rel="noopener noreferrer"&gt;https://react.dev/learn&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>architecture</category>
      <category>dotnet</category>
      <category>react</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Análisis de vulnerabilidades y despliegue automatizado de una app con Bearer CLI y ESLint</title>
      <dc:creator>Gabriela Estefania Cohaila Alvarado</dc:creator>
      <pubDate>Sun, 04 Oct 2026 10:05:34 +0000</pubDate>
      <link>https://dev.to/gabrielacohailaalvaradoe/analisis-de-vulnerabilidades-y-despliegue-automatizado-de-una-app-con-bearer-cli-y-eslint-14ij</link>
      <guid>https://dev.to/gabrielacohailaalvaradoe/analisis-de-vulnerabilidades-y-despliegue-automatizado-de-una-app-con-bearer-cli-y-eslint-14ij</guid>
      <description>&lt;p&gt;&lt;strong&gt;Autores del artículo:&lt;br&gt;
Gabriela Estefania Cohaila Alvarado (2022075746)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Integrantes:&lt;br&gt;
Gabriela Estefania Cohaila Alvarado (2022075746)&lt;br&gt;
Jhony Vargas Luque (2022075754)&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;En este proyecto construimos &lt;strong&gt;Notas Seguras&lt;/strong&gt;, una aplicación de tareas en JavaScript, para estudiar cómo detectar y corregir problemas de seguridad antes de publicar una nueva versión. Comparamos implementaciones educativas inseguras con el código corregido mediante &lt;strong&gt;Bearer CLI&lt;/strong&gt; y &lt;strong&gt;ESLint con reglas de seguridad&lt;/strong&gt;, y conectamos los controles con un despliegue automatizado en &lt;strong&gt;GitHub Pages&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Los resultados fueron concretos: Bearer reportó dos hallazgos altos en los componentes vulnerables; ESLint señaló esos mismos puntos y un uso adicional de &lt;code&gt;eval&lt;/code&gt;. Después de corregir el código, ambos analizadores terminaron sin hallazgos en la aplicación publicable y las cinco pruebas de regresión pasaron. En este artículo presentamos conjuntamente los hallazgos, las correcciones y la automatización.&lt;/p&gt;
&lt;h2&gt;
  
  
  Código y aplicación
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://github.com/GabrielaCohailaAlvaradoE/notas-seguras-sast" rel="noopener noreferrer"&gt;Repositorio público de Notas Seguras&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://gabrielacohailaalvaradoe.github.io/notas-seguras-sast/" rel="noopener noreferrer"&gt;Aplicación pública en GitHub Pages&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/GabrielaCohailaAlvaradoE/notas-seguras-sast/actions/runs/37145288144" rel="noopener noreferrer"&gt;Ejecución comprobada del análisis y despliegue&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/GabrielaCohailaAlvaradoE/notas-seguras-sast/tree/main/evidence" rel="noopener noreferrer"&gt;Informes de la ejecución de referencia&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  La aplicación y el alcance del experimento
&lt;/h2&gt;

&lt;p&gt;Notas Seguras permite crear tareas con título, descripción, prioridad y minutos estimados; también buscar, filtrar, completar y eliminar tareas. Está desarrollada con HTML, CSS y JavaScript. Guarda la información mediante &lt;code&gt;localStorage&lt;/code&gt; en el navegador, sin cuentas, sincronización ni backend de negocio. GitHub Pages sirve los archivos estáticos de la aplicación por HTTPS.&lt;/p&gt;

&lt;p&gt;El repositorio separa dos alcances. &lt;code&gt;examples/vulnerable/&lt;/code&gt; conserva las implementaciones inseguras de tres funciones para realizar la comparación. &lt;code&gt;app/&lt;/code&gt; contiene la aplicación completa con esas funciones corregidas. La baseline es un ejemplo educativo de componentes, no una aplicación anterior que se haya publicado. El escaneo inicial cubre esos componentes; el análisis final cubre toda la aplicación publicable.&lt;/p&gt;
&lt;h2&gt;
  
  
  Selección de herramientas distintas a los laboratorios
&lt;/h2&gt;

&lt;p&gt;Revisamos las cuatro guías de laboratorio compartidas: lab01 utiliza SonarQube/SonarCloud; lab02, Snyk; lab03, Semgrep; y lab04, tfsec y SonarCloud. Para este trabajo elegimos &lt;strong&gt;Bearer CLI 2.1.1&lt;/strong&gt; y &lt;strong&gt;ESLint 10.12.0 con eslint-plugin-no-unsanitized 4.1.5&lt;/strong&gt;, que no aparecen en esas cuatro guías.&lt;/p&gt;

&lt;p&gt;Bearer figura en el &lt;a href="https://community.owasp.org/Source_Code_Analysis_Tools" rel="noopener noreferrer"&gt;catálogo de analizadores de código de OWASP&lt;/a&gt;. También consultamos el &lt;a href="https://www.nist.gov/itl/csd/secure-systems-and-applications/source-code-security-analyzers" rel="noopener noreferrer"&gt;directorio de analizadores de NIST&lt;/a&gt; como referencia. La inclusión en un catálogo no certifica la seguridad del proyecto ni implica un aval de sus resultados.&lt;/p&gt;

&lt;p&gt;ESLint es un analizador configurable. En nuestro caso, el plugin de Mozilla prohíbe operaciones de inserción HTML inseguras y las reglas de ESLint restringen evaluación dinámica de JavaScript. Lo usamos como control complementario de patrones de seguridad, sin atribuirle una auditoría general de todos los riesgos de la aplicación. Esta configuración no requiere una cuenta de escaneo.&lt;/p&gt;
&lt;h2&gt;
  
  
  Preparación y ejecución de los análisis
&lt;/h2&gt;

&lt;p&gt;Con Node.js 24 instalado, se puede reproducir el proyecto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/GabrielaCohailaAlvaradoE/notas-seguras-sast.git
&lt;span class="nb"&gt;cd &lt;/span&gt;notas-seguras-sast
npm ci
npm &lt;span class="nb"&gt;test
&lt;/span&gt;npm run scan:eslint
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para ejecutar Bearer, utilizar Linux x86_64 o Ubuntu en WSL. Desde la raíz del mismo repositorio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;bash scripts/install-bearer.sh
bash scripts/scan-bearer.sh
node scripts/summarize.mjs
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El instalador descarga Bearer 2.1.1 de su publicación oficial y verifica el archivo con la suma SHA256 publicada para esa versión. El script analiza primero &lt;code&gt;examples/vulnerable/&lt;/code&gt; y después &lt;code&gt;app/&lt;/code&gt;, genera informes JSON y produce también SARIF para el código corregido. &lt;code&gt;reports/&lt;/code&gt; contiene los resultados de cada ejecución; &lt;code&gt;evidence/&lt;/code&gt; conserva una instantánea verificable de la ejecución enlazada.&lt;/p&gt;

&lt;p&gt;Para abrir la aplicación localmente, ejecutar &lt;code&gt;npm start&lt;/code&gt; y visitar &lt;code&gt;http://127.0.0.1:4173&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hallazgo 1 y 2: inserción de texto como HTML
&lt;/h2&gt;

&lt;p&gt;Las implementaciones vulnerables del título y la descripción usaban este patrón:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El navegador interpreta el contenido asignado a &lt;code&gt;innerHTML&lt;/code&gt; como marcado. Si esa entrada está controlada por un atacante, puede introducir contenido activo y generar riesgo de XSS. En esta aplicación local, la exposición depende de cómo llegue la entrada al navegador; no afirmamos que exista un servidor multiusuario comprometido.&lt;/p&gt;

&lt;p&gt;Bearer reportó dos hallazgos altos de la regla &lt;code&gt;javascript_lang_dangerous_insert_html&lt;/code&gt;, asociados a &lt;strong&gt;CWE-79&lt;/strong&gt;, en las líneas 4 y 7 de &lt;code&gt;examples/vulnerable/rendering.js&lt;/code&gt;. ESLint señaló las mismas asignaciones con &lt;code&gt;no-unsanitized/property&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Las tareas no necesitan contenido HTML enriquecido, por lo que corregimos ambas funciones con &lt;code&gt;textContent&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;renderTitle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;element&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;textContent&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Con este cambio, los datos se presentan como texto. Una entrada como &lt;code&gt;&amp;lt;b&amp;gt;Prueba de texto&amp;lt;/b&amp;gt;&lt;/code&gt; se muestra literalmente, sin convertirse en marcado. Esta comprobación visual relaciona la corrección con un comportamiento observable.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hallazgo 3: evaluación dinámica de los minutos
&lt;/h2&gt;

&lt;p&gt;El componente vulnerable para interpretar minutos utilizaba:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;eval&lt;/code&gt; puede interpretar la entrada como JavaScript, aunque el propósito del campo sea registrar una duración. La regla &lt;code&gt;no-eval&lt;/code&gt; de ESLint detectó esta operación en la línea 4 de &lt;code&gt;examples/vulnerable/core.js&lt;/code&gt;. &lt;strong&gt;Bearer no la reportó en este ejemplo&lt;/strong&gt;; no le atribuimos ese hallazgo.&lt;/p&gt;

&lt;p&gt;La corrección elimina la evaluación de expresiones y valida el dominio permitido:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;parseMinutes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;trim&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="sr"&gt;/^&lt;/span&gt;&lt;span class="se"&gt;\d{1,4}&lt;/span&gt;&lt;span class="sr"&gt;$/&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;test&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Usa minutos enteros entre 1 y 1440.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Number&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;minutes&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;1440&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Usa minutos enteros entre 1 y 1440.&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;minutes&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Una entrada como &lt;code&gt;25&lt;/code&gt; es válida; &lt;code&gt;20+5&lt;/code&gt;, valores fuera de rango o código JavaScript se rechazan. El producto necesita minutos enteros, no una calculadora de expresiones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados después de corregir
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Punto del código vulnerable&lt;/th&gt;
&lt;th&gt;Bearer CLI&lt;/th&gt;
&lt;th&gt;ESLint&lt;/th&gt;
&lt;th&gt;Corrección&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Título, &lt;code&gt;rendering.js:4&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;XSS alto, CWE-79&lt;/td&gt;
&lt;td&gt;&lt;code&gt;no-unsanitized/property&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;textContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Descripción, &lt;code&gt;rendering.js:7&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;XSS alto, CWE-79&lt;/td&gt;
&lt;td&gt;&lt;code&gt;no-unsanitized/property&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;textContent&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Minutos, &lt;code&gt;core.js:4&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;No reportado en este caso&lt;/td&gt;
&lt;td&gt;&lt;code&gt;no-eval&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Validación y &lt;code&gt;Number&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Medición&lt;/th&gt;
&lt;th&gt;Componentes vulnerables&lt;/th&gt;
&lt;th&gt;Aplicación corregida&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Hallazgos de Bearer&lt;/td&gt;
&lt;td&gt;2 altos&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Errores de reglas de ESLint&lt;/td&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Pruebas de regresión aprobadas&lt;/td&gt;
&lt;td&gt;No aplica&lt;/td&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Son &lt;strong&gt;tres puntos inseguros&lt;/strong&gt;, no cinco vulnerabilidades diferentes: los dos analizadores coinciden en las inserciones HTML. Tampoco se debe equiparar un error de regla de ESLint con la escala de severidad de Bearer.&lt;/p&gt;

&lt;p&gt;Las pruebas comprueban límites válidos de minutos, rechazo de expresiones y código, uso de texto en ambos renderizadores, manejo de JSON local dañado y filtrado de registros inválidos con un límite de almacenamiento. La app incluye además una política CSP, pero la desaparición de las alertas se debe a corregir las funciones, no a ocultar los hallazgos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Automatización de los controles y del despliegue
&lt;/h2&gt;

&lt;p&gt;El archivo &lt;code&gt;.github/workflows/security-and-deploy.yml&lt;/code&gt; se activa con pushes a &lt;code&gt;main&lt;/code&gt;, solicitudes de cambio y ejecuciones manuales. Su trabajo &lt;code&gt;security&lt;/code&gt; realiza los siguientes pasos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Descarga el código e instala Node.js 24.&lt;/li&gt;
&lt;li&gt;Reproduce las dependencias con &lt;code&gt;npm ci&lt;/code&gt; y ejecuta las cinco pruebas.&lt;/li&gt;
&lt;li&gt;Analiza la baseline y la aplicación con ESLint.&lt;/li&gt;
&lt;li&gt;Instala Bearer, verifica SHA256 y analiza ambos alcances.&lt;/li&gt;
&lt;li&gt;Genera el resumen y guarda el artefacto &lt;code&gt;security-reports&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Construye la app y prepara los archivos para Pages.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;El despliegue se ejecuta después y únicamente para &lt;code&gt;main&lt;/code&gt;, fuera de las solicitudes de cambio. La dependencia fundamental es:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;security&lt;/span&gt;
  &lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;pages&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
    &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Si falla el trabajo de seguridad, no se publica esa nueva versión. El sitio previamente desplegado puede seguir disponible. El trabajo de análisis tiene permiso de lectura; los permisos de publicación se conceden en el trabajo de despliegue.&lt;/p&gt;

&lt;p&gt;La baseline contiene problemas intencionales, así que el script de Bearer espera código de salida 1 en ese análisis. En cambio, el escaneo de &lt;code&gt;app/&lt;/code&gt; debe finalizar con éxito normal. No se fuerza &lt;code&gt;--exit-code 0&lt;/code&gt; ni se aplica &lt;code&gt;continue-on-error&lt;/code&gt; al control de la aplicación publicable. El comparador de ESLint exige tres errores en los componentes educativos y cero en la app.&lt;/p&gt;

&lt;p&gt;La construcción copia exclusivamente seis archivos de &lt;code&gt;app/&lt;/code&gt;; no publica los ejemplos vulnerables, los scripts ni &lt;code&gt;node_modules&lt;/code&gt;. Las acciones de GitHub están fijadas por SHA de commit y las dependencias de Node por versión y lockfile. Los informes de Actions se conservan siete días, y la copia versionada en &lt;code&gt;evidence/&lt;/code&gt; conserva la ejecución de referencia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Publicación en un proveedor SaaS público
&lt;/h2&gt;

&lt;p&gt;Configuramos &lt;strong&gt;GitHub Actions&lt;/strong&gt; como origen de publicación en Settings → Pages. &lt;strong&gt;GitHub Pages&lt;/strong&gt; aloja la aplicación estática mediante HTTPS. El proyecto utiliza un repositorio público y runners estándar &lt;code&gt;ubuntu-latest&lt;/code&gt;, sin contratar servicios de pago ni configurar una clave personal de nube en el código.&lt;/p&gt;

&lt;p&gt;La ejecución enlazada terminó con &lt;code&gt;security&lt;/code&gt; y &lt;code&gt;deploy&lt;/code&gt; en estado satisfactorio. El sitio respondió HTTP 200 y verificamos en el navegador la creación de tareas y la presentación literal de etiquetas. Así, la evidencia conecta el código, el análisis, la automatización y la aplicación disponible públicamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusiones del equipo
&lt;/h2&gt;

&lt;p&gt;La comparación mostró una diferencia útil de cobertura: Bearer detectó las dos inserciones HTML inseguras y ESLint añadió el uso de &lt;code&gt;eval&lt;/code&gt;. Corregir las operaciones y comprobar el comportamiento permitió pasar de &lt;strong&gt;2 a 0 hallazgos en Bearer&lt;/strong&gt;, de &lt;strong&gt;3 a 0 errores en ESLint&lt;/strong&gt; y obtener &lt;strong&gt;cinco pruebas aprobadas&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Integrar estos controles antes del despliegue permite que sus resultados influyan en lo que se publica. Sin embargo, cero hallazgos describe las reglas, versiones y alcance utilizados; no demuestra seguridad absoluta. Quedan fuera una auditoría completa, pruebas dinámicas y una evaluación SCA como parte de esta comparación. La aplicación guarda notas locales sin cifrado propio y no debe utilizarse para información sensible.&lt;/p&gt;

&lt;h2&gt;
  
  
  Referencias técnicas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://community.owasp.org/Source_Code_Analysis_Tools" rel="noopener noreferrer"&gt;OWASP — Source Code Analysis Tools&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.nist.gov/itl/csd/secure-systems-and-applications/source-code-security-analyzers" rel="noopener noreferrer"&gt;NIST — Source Code Security Analyzers&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.bearer.com/reference/commands/" rel="noopener noreferrer"&gt;Documentación y comandos de Bearer CLI&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.bearer.com/reference/rules/javascript_lang_dangerous_insert_html/" rel="noopener noreferrer"&gt;Regla de inserción HTML de Bearer&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://github.com/mozilla/eslint-plugin-no-unsanitized" rel="noopener noreferrer"&gt;Plugin no-unsanitized de Mozilla&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://eslint.org/docs/latest/rules/no-eval" rel="noopener noreferrer"&gt;Regla no-eval de ESLint&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/pages/getting-started-with-github-pages/using-custom-workflows-with-github-pages" rel="noopener noreferrer"&gt;Workflows personalizados para GitHub Pages&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://docs.github.com/en/billing/concepts/product-billing/github-actions" rel="noopener noreferrer"&gt;Uso y costos de GitHub Actions&lt;/a&gt;.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
