<?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: STEVIE GERALD MARCA AGUILAR</title>
    <description>The latest articles on DEV Community by STEVIE GERALD MARCA AGUILAR (@stevie_geraldmarcaaguil).</description>
    <link>https://dev.to/stevie_geraldmarcaaguil</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%2F4160751%2F6d286810-ded3-4722-8f06-a740f0492310.png</url>
      <title>DEV Community: STEVIE GERALD MARCA AGUILAR</title>
      <link>https://dev.to/stevie_geraldmarcaaguil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/stevie_geraldmarcaaguil"/>
    <language>en</language>
    <item>
      <title>Seguridad automatizada en la nube: integración de Semgrep, Gitleaks y Trivy en un pipeline CI/CD</title>
      <dc:creator>STEVIE GERALD MARCA AGUILAR</dc:creator>
      <pubDate>Fri, 16 Oct 2026 14:36:22 +0000</pubDate>
      <link>https://dev.to/stevie_geraldmarcaaguil/seguridad-automatizada-en-la-nube-integracion-de-semgrep-gitleaks-y-trivy-en-un-pipeline-cicd-1an</link>
      <guid>https://dev.to/stevie_geraldmarcaaguil/seguridad-automatizada-en-la-nube-integracion-de-semgrep-gitleaks-y-trivy-en-un-pipeline-cicd-1an</guid>
      <description>&lt;p&gt;&lt;strong&gt;Por: Stevie Gerald Marca Aguilar y Usher Damiron Halanocca Rojas&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Durante nuestro proyecto, integramos &lt;strong&gt;Semgrep, Gitleaks y Trivy en un pipeline de GitHub Actions&lt;/strong&gt;, conectado al despliegue de una aplicación en Render. El propósito fue establecer una revisión automática de seguridad antes de autorizar la publicación de una nueva versión.&lt;/p&gt;

&lt;p&gt;En este artículo explico cómo organizamos la solución, qué función cumple cada herramienta y qué aprendimos al analizar una aplicación con vulnerabilidades incorporadas intencionalmente.&lt;/p&gt;

&lt;h2&gt;
  
  
  ¿Por qué incorporar seguridad al pipeline?
&lt;/h2&gt;

&lt;p&gt;Durante el desarrollo pueden aparecer problemas que pasan desapercibidos en una revisión funcional. Por ejemplo, una aplicación puede permitir iniciar sesión correctamente y, al mismo tiempo, construir consultas SQL mediante concatenación o tener una clave privada escrita en sus archivos.&lt;/p&gt;

&lt;p&gt;Entre los riesgos que consideramos estuvieron:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Credenciales y tokens almacenados directamente en el repositorio.&lt;/li&gt;
&lt;li&gt;Dependencias con vulnerabilidades publicadas.&lt;/li&gt;
&lt;li&gt;Consultas SQL construidas con datos ingresados por el usuario.&lt;/li&gt;
&lt;li&gt;Ejecución de código mediante funciones inseguras.&lt;/li&gt;
&lt;li&gt;Registro de información sensible en los mensajes de la aplicación.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Revisar estos aspectos manualmente en cada cambio requiere tiempo y puede producir omisiones. Por ello, incorporamos herramientas que ejecutan controles de forma automática y entregan resultados para su revisión.&lt;/p&gt;

&lt;h2&gt;
  
  
  Las herramientas utilizadas
&lt;/h2&gt;

&lt;p&gt;Seleccionamos tres herramientas de código abierto que cubren aspectos diferentes del proyecto.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Herramienta&lt;/th&gt;
&lt;th&gt;Tipo de análisis&lt;/th&gt;
&lt;th&gt;Propósito en el proyecto&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Semgrep&lt;/td&gt;
&lt;td&gt;Análisis estático de código, SAST&lt;/td&gt;
&lt;td&gt;Identificar patrones de programación potencialmente inseguros.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gitleaks&lt;/td&gt;
&lt;td&gt;Detección de secretos&lt;/td&gt;
&lt;td&gt;Buscar credenciales y otros datos sensibles en el repositorio.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trivy&lt;/td&gt;
&lt;td&gt;Análisis de dependencias y configuración&lt;/td&gt;
&lt;td&gt;Identificar vulnerabilidades conocidas y configuraciones inseguras.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Semgrep: revisión del código fuente
&lt;/h3&gt;

&lt;p&gt;Semgrep analiza el código mediante reglas que describen patrones que queremos identificar. En nuestro caso, lo utilizamos para revisar una aplicación desarrollada con Node.js y Express.&lt;/p&gt;

&lt;p&gt;El análisis se orientó a encontrar construcciones como consultas SQL inseguras, uso de &lt;code&gt;eval()&lt;/code&gt; y otras prácticas que pueden comprometer la aplicación.&lt;/p&gt;

&lt;p&gt;Su utilidad depende de las reglas seleccionadas y del contexto del código. Por eso, los resultados necesitan una revisión para determinar cuáles representan problemas reales.&lt;/p&gt;

&lt;h3&gt;
  
  
  Gitleaks: búsqueda de credenciales expuestas
&lt;/h3&gt;

&lt;p&gt;Gitleaks permite detectar posibles secretos, como tokens, claves de acceso y credenciales incluidas en archivos versionados.&lt;/p&gt;

&lt;p&gt;En el proyecto lo empleamos para revisar el repositorio y su historial disponible. Esto es relevante porque eliminar una contraseña del archivo actual no significa que desaparezca de los commits anteriores.&lt;/p&gt;

&lt;p&gt;La detección tampoco debe considerarse infalible: algunas credenciales requieren reglas específicas y ciertos ejemplos pueden generar alertas que necesitan revisión.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trivy: revisión de dependencias y configuraciones
&lt;/h3&gt;

&lt;p&gt;Trivy permite analizar distintas partes de una aplicación, entre ellas sus dependencias, imágenes de contenedores y archivos de configuración compatibles.&lt;/p&gt;

&lt;p&gt;En nuestra implementación lo utilizamos principalmente para identificar vulnerabilidades conocidas en las bibliotecas del proyecto. También exploramos el análisis de configuraciones, como las incluidas en un Dockerfile.&lt;/p&gt;

&lt;p&gt;Sus reportes ayudan a reconocer qué componente está afectado y, cuando existe información disponible, qué versión incorpora una corrección.&lt;/p&gt;

&lt;h2&gt;
  
  
  La aplicación de demostración
&lt;/h2&gt;

&lt;p&gt;Para evaluar las herramientas desarrollamos una API REST con Node.js y Express. La aplicación incluía funciones de autenticación, administración de usuarios y búsqueda.&lt;/p&gt;

&lt;p&gt;Incorporamos errores de seguridad de manera intencional para observar el comportamiento de los escáneres.&lt;/p&gt;

&lt;p&gt;Uno de los casos consistió en declarar un secreto directamente en el código:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;clave_de_prueba_insegura&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;También incluimos una consulta SQL construida con datos del usuario:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
  &lt;span class="s2"&gt;`SELECT * FROM users WHERE username = '&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;'`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Otro ejemplo fue la ejecución dinámica de una transformació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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;eval&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`(&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;transform&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;)`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finalmente, agregamos registros que exponían contraseñas y versiones antiguas de algunas dependencias.&lt;/p&gt;

&lt;p&gt;Estos casos permitieron comparar los distintos enfoques de análisis. Cada herramienta revisó una parte diferente del problema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Ejecución de los análisis
&lt;/h2&gt;

&lt;p&gt;Antes de configurar la automatización, ejecutamos las herramientas localmente para comprender sus resultados.&lt;/p&gt;

&lt;h3&gt;
  
  
  Análisis con Semgrep
&lt;/h3&gt;

&lt;p&gt;Utilizamos conjuntos de reglas relacionados con Node.js, JavaScript y riesgos de seguridad web:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;semgrep

semgrep &lt;span class="nt"&gt;--config&lt;/span&gt; &lt;span class="s2"&gt;"p/nodejs"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--config&lt;/span&gt; &lt;span class="s2"&gt;"p/javascript"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        &lt;span class="nt"&gt;--config&lt;/span&gt; &lt;span class="s2"&gt;"p/owasp-top-ten"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
        app/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La revisión permitió identificar patrones que debían evaluarse y corregirse dentro de la aplicación.&lt;/p&gt;

&lt;h3&gt;
  
  
  Análisis con Gitleaks
&lt;/h3&gt;

&lt;p&gt;En la implementación descrita utilizamos el siguiente comando:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gitleaks detect &lt;span class="nt"&gt;--source&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt; &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;El objetivo fue buscar posibles credenciales expuestas en el repositorio. El alcance efectivo de la revisión depende del historial disponible y de la configuración del escaneo.&lt;/p&gt;

&lt;h3&gt;
  
  
  Análisis con Trivy
&lt;/h3&gt;

&lt;p&gt;Para revisar las dependencias ejecutamos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;trivy fs ./app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;También realizamos un análisis de configuración:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;trivy config &lt;span class="nb"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Estos resultados permitieron identificar componentes y configuraciones que requerían atención.&lt;/p&gt;

&lt;h2&gt;
  
  
  Correcciones realizadas
&lt;/h2&gt;

&lt;p&gt;Después de revisar los hallazgos, aplicamos cambios en el código y en la gestión de dependencias.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Uso de variables de entorno
&lt;/h3&gt;

&lt;p&gt;Reemplazamos los secretos escritos directamente en los archivos por valores obtenidos desde el entorno:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;JWT_SECRET&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;JWT_SECRET&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="nx"&gt;JWT_SECRET&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;La variable JWT_SECRET no está configurada&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto permite configurar la credencial sin incluirla en el código fuente. Si una credencial real ya fue expuesta, también debe revocarse o reemplazarse; retirarla del archivo no resuelve por sí solo la exposición anterior.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Consultas parametrizadas
&lt;/h3&gt;

&lt;p&gt;Sustituimos la concatenación de datos por parámetros:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;query&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;SELECT * FROM users WHERE username = ?&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="nx"&gt;callback&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Así, el valor ingresado se proporciona por separado de la estructura de la consulta.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Sustitución de &lt;code&gt;eval()&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Reemplazamos la ejecución dinámica por un conjunto definido de operaciones permitidas:&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="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;TRANSFORMS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;uppercase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;username&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;user&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;username&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toUpperCase&lt;/span&gt;&lt;span class="p"&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;fn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;TRANSFORMS&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;transform&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="nx"&gt;fn&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;Transformación no permitida&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De esta manera, la aplicación selecciona una operación previamente implementada.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Actualización de dependencias
&lt;/h3&gt;

&lt;p&gt;Revisamos las bibliotecas señaladas por Trivy y actualizamos las versiones correspondientes. Después repetimos los análisis para comprobar el efecto de las correcciones.&lt;/p&gt;

&lt;h2&gt;
  
  
  Resultados de la experiencia
&lt;/h2&gt;

&lt;p&gt;Los resultados reportados en el proyecto mostraron una reducción de los hallazgos después de aplicar los cambios.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Herramienta&lt;/th&gt;
&lt;th&gt;Resultado inicial reportado&lt;/th&gt;
&lt;th&gt;Resultado posterior reportado&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Semgrep&lt;/td&gt;
&lt;td&gt;9 hallazgos&lt;/td&gt;
&lt;td&gt;1 hallazgo, considerado falso positivo tras su revisión.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Gitleaks&lt;/td&gt;
&lt;td&gt;3 posibles secretos&lt;/td&gt;
&lt;td&gt;0 hallazgos en la revisión posterior.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Trivy&lt;/td&gt;
&lt;td&gt;4 vulnerabilidades&lt;/td&gt;
&lt;td&gt;0 vulnerabilidades críticas reportadas.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;En Trivy, &lt;strong&gt;no encontrar vulnerabilidades críticas no equivale a no tener vulnerabilidades de otras severidades&lt;/strong&gt;. La interpretación del resultado debe considerar los filtros utilizados.&lt;/p&gt;

&lt;p&gt;Asimismo, la ausencia de alertas en cualquiera de las herramientas no garantiza que la aplicación esté libre de problemas de seguridad.&lt;/p&gt;

&lt;h2&gt;
  
  
  Integración con GitHub Actions
&lt;/h2&gt;

&lt;p&gt;Una vez realizadas las pruebas locales, incorporamos los análisis al flujo automatizado del repositorio.&lt;/p&gt;

&lt;p&gt;El pipeline se configuró para ejecutarse con los cambios enviados a la rama &lt;code&gt;main&lt;/code&gt;. Organizamos el proceso en cuatro trabajos:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Análisis estático con Semgrep.&lt;/li&gt;
&lt;li&gt;Detección de secretos con Gitleaks.&lt;/li&gt;
&lt;li&gt;Revisión de dependencias con Trivy.&lt;/li&gt;
&lt;li&gt;Solicitud de despliegue a Render.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;La dependencia del trabajo de despliegue se expresó de esta forma:&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="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;semgrep&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;gitleaks&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;trivy&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esto hace que ese trabajo espere la finalización satisfactoria de los tres análisis.&lt;/p&gt;

&lt;p&gt;Para disponer del historial completo durante la revisión del repositorio, utilizamos:&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;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;fetch-depth&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;En el análisis de Trivy configuramos el bloqueo ante vulnerabilidades críticas:&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;severity&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;CRITICAL'&lt;/span&gt;
&lt;span class="na"&gt;exit-code&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;1'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por tanto, el criterio de aprobación de este análisis estuvo limitado a dicha severidad. Los hallazgos de niveles inferiores requieren una política adicional si también se desea bloquear el despliegue por ellos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Despliegue de la aplicación en Render
&lt;/h2&gt;

&lt;p&gt;Utilizamos Render como plataforma de despliegue de la API. La configuración del servicio definió el directorio de la aplicación, los comandos de instalación y ejecución, y la ruta de comprobación de salud.&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;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;web&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;vulnerable-demo-api&lt;/span&gt;
    &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node&lt;/span&gt;
    &lt;span class="na"&gt;rootDir&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt;
    &lt;span class="na"&gt;buildCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;npm install&lt;/span&gt;
    &lt;span class="na"&gt;startCommand&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;node server.js&lt;/span&gt;
    &lt;span class="na"&gt;healthCheckPath&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;/health&lt;/span&gt;
    &lt;span class="na"&gt;envVars&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;JWT_SECRET&lt;/span&gt;
        &lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;La solicitud de despliegue se realizó mediante un webhook cuya dirección se almacenó en GitHub Secrets como &lt;code&gt;RENDER_DEPLOY_HOOK_URL&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;El flujo planteado fue que GitHub Actions solicitara la publicación después de aprobar los controles. Para que esta condición se cumpla en toda la solución, la configuración de Render también debe evitar despliegues automáticos que omitan ese flujo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Aprendizajes del proyecto
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Integrar seguridad desde el desarrollo facilita detectar problemas antes de publicar.&lt;/strong&gt; Los análisis automáticos aportan una revisión repetible en cada ejecución del pipeline.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Las herramientas se complementan.&lt;/strong&gt; Semgrep revisa patrones del código, Gitleaks busca posibles secretos y Trivy analiza componentes y configuraciones compatibles.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Los reportes necesitan interpretación.&lt;/strong&gt; Una alerta debe revisarse considerando el contexto, mientras que un análisis aprobado debe entenderse según sus reglas, filtros y alcance.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;La gestión de secretos requiere más que eliminarlos del código.&lt;/strong&gt; También es necesario proteger su almacenamiento y reemplazar cualquier credencial real que haya quedado expuesta.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mantener las dependencias actualizadas es una tarea continua.&lt;/strong&gt; Una versión sin alertas hoy puede presentar vulnerabilidades conocidas posteriormente.&lt;/p&gt;

&lt;p&gt;Este proyecto nos permitió incorporar controles de seguridad al proceso de entrega de software y comprender cómo conectarlos con un despliegue en la nube. Además de ejecutar herramientas, aprendimos a revisar sus resultados y a definir qué condiciones deben cumplirse antes de publicar una nueva versión.&lt;/p&gt;

&lt;p&gt;Aplicativo Prueba: &lt;a href="https://articulo-marca-usher.onrender.com/" rel="noopener noreferrer"&gt;https://articulo-marca-usher.onrender.com/&lt;/a&gt;&lt;br&gt;
Video:&lt;a href="https://studio.youtube.com/video/1DI_BEcRcOk/edit" rel="noopener noreferrer"&gt;https://studio.youtube.com/video/1DI_BEcRcOk/edit&lt;/a&gt;&lt;br&gt;
Git Hub: &lt;a href="https://github.com/Stevien04/Articulo-Marca-Usher.git" rel="noopener noreferrer"&gt;https://github.com/Stevien04/Articulo-Marca-Usher.git&lt;/a&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>cicd</category>
      <category>devops</category>
      <category>security</category>
    </item>
    <item>
      <title>Respaldo y recuperación de MySQL con Railway Aiven y GitHub Actions</title>
      <dc:creator>STEVIE GERALD MARCA AGUILAR</dc:creator>
      <pubDate>Sun, 04 Oct 2026 05:06:11 +0000</pubDate>
      <link>https://dev.to/stevie_geraldmarcaaguil/respaldo-y-recuperacion-de-mysql-con-railway-aiven-y-github-actions-44o6</link>
      <guid>https://dev.to/stevie_geraldmarcaaguil/respaldo-y-recuperacion-de-mysql-con-railway-aiven-y-github-actions-44o6</guid>
      <description>&lt;p&gt;Integrantes: &lt;br&gt;
Stevie Gerald Marca Aguilar&lt;br&gt;
Soanny Geraldine Rivas Gutierrez&lt;/p&gt;

&lt;p&gt;INTRODUCCIÓN&lt;br&gt;
Un inventario necesita conservar información fiable sobre las existencias y los precios. Si se elimina un registro o se modifica por error, una copia anterior permite recuperar su contenido. El código del repositorio reconstruye la aplicación, pero recuperar los datos requiere un respaldo de MySQL.&lt;br&gt;
El proyecto combina una aplicación de inventario en Node.js y Express con una base MySQL. Los scripts generan una exportación completa, la comprimen y la protegen mediante cifrado. GitHub Actions incluye un flujo diario de respaldo, mientras Railway publica la aplicación y la configuración contempla una conexión a MySQL administrado en Aiven.&lt;br&gt;
El documento describe la arquitectura, la preparación del entorno y el procedimiento de recuperación. Incluye las pruebas locales reportadas y las capturas de la publicación del repositorio y de una ejecución manual del backup. La aplicación pública responde y confirma su conexión con MySQL; la ejecución diaria programada debe comprobarse en el historial de Actions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1.    Arquitectura del inventario&lt;/strong&gt;&lt;br&gt;
El inventario permite listar productos, registrar nuevas entradas y eliminar registros. Cada producto contiene ID, nombre, precio, cantidad disponible y fecha de creación. Este modelo facilita comparar la información original con los datos obtenidos después de restaurar una copia.&lt;/p&gt;

&lt;p&gt;La Tabla 1 presenta componentes y responsabilidades de la solución.&lt;br&gt;
Tabla 1&lt;br&gt;
Componentes y responsabilidades de la solución&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl189i3us53oq21ppn0ji.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fl189i3us53oq21ppn0ji.png" alt=" " width="699" height="401"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Las pruebas de desarrollo se realizan en el entorno local. Railway ejecuta la API en la nube y la configuración de conexión permite utilizar MySQL en Aiven. El proceso de Actions accede a la base configurada y conserva la copia como artefacto cifrado. El repositorio almacena el código; los archivos de respaldo se descargan aparte y tienen una retención limitada.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Criterios para conservar las copias&lt;/strong&gt;
Para esta base de demostración se eligió una exportación lógica completa. La copia reúne las tablas y sus registros en un archivo SQL que puede revisarse después del descifrado. El procedimiento no incluye respaldos incrementales ni recuperación a un instante específico mediante binlogs.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;La Tabla 2 reúne la frecuencia, la conservación y las condiciones necesarias para recuperar una copia.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F65xubmvbzud2ruiuadfi.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F65xubmvbzud2ruiuadfi.png" alt=" " width="717" height="466"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Una copia diaria exitosa permite recuperar el estado guardado en la última ejecución. Por ello, podrían perderse los cambios posteriores, aproximadamente los de un día si se cumple el horario. Los fallos o retrasos aumentan ese intervalo. El tiempo de recuperación debe medirse con la cantidad real de datos; este proyecto no garantiza un RTO concreto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Organización y puesta en marcha&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El proyecto organiza la API, la interfaz y las herramientas de mantenimiento en carpetas separadas. Los archivos de configuración permiten reproducir el entorno local y definir los procesos de pruebas, publicación y respaldo. La Tabla 3 identifica sus elementos principales.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxrjerfegs8pj72el3h8l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fxrjerfegs8pj72el3h8l.png" alt=" " width="601" height="557"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8q3do995gsad49gfb65a.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F8q3do995gsad49gfb65a.png" alt=" " width="710" height="483"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;En Windows se prepara el archivo de variables a partir de la plantilla y se asignan claves propias. Docker Desktop debe utilizar contenedores Linux. Desde la carpeta del proyecto, estos comandos levantan los servicios y consultan su estado.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0tnxomeen38vzcc9i61z.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F0tnxomeen38vzcc9i61z.png" alt=" " width="416" height="121"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Los datos de MySQL permanecen en un volumen de Docker. MYSQL_DATABASE, MYSQL_USER y MYSQL_PASSWORD se aplican al inicializar un volumen nuevo. Cambiar esos valores posteriormente no actualiza las cuentas existentes. La aplicación establece la conexión con DB_HOST, DB_PORT, DB_USER, DB_PASSWORD y DB_NAME.&lt;br&gt;
La configuración de laboratorio admite DB_SSL=false en la red local. La conexión externa a Aiven requiere DB_SSL=true y su certificado CA. La comprobación TLS debe validar la confianza del certificado y el nombre del servidor.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;4.Estructura de datos y operaciones de la API&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;La tabla productos usa InnoDB y guarda el precio con dos decimales. El stock no admite cantidades negativas. El SQL actúa sobre la base seleccionada por la conexión, sin imponer un nombre mediante USE; así puede importarse en una base de recuperación distinta.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F90admr1qt5e11hrlgtn1.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F90admr1qt5e11hrlgtn1.png" alt=" " width="683" height="320"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La API ofrece consultas públicas y operaciones de escritura protegidas. Registrar o eliminar productos exige enviar X-API-Key, cuyo valor se define en ADMIN_API_KEY. También se comprueba que el precio sea numérico y no negativo, con hasta dos decimales, y que el stock sea un entero no negativo.&lt;/p&gt;

&lt;p&gt;Las rutas y sus funciones se presentan en la Tabla 4.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk11hkvak7u192c2j33tc.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fk11hkvak7u192c2j33tc.png" alt=" " width="695" height="332"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuchaia28twkieq7c2114.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fuchaia28twkieq7c2114.png" alt=" " width="682" height="799"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwlcrip5wpa9nyyo6s3wx.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwlcrip5wpa9nyyo6s3wx.png" alt=" " width="540" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5.Generación y protección del respaldo&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Antes de exportar, backup.sh comprueba la configuración y crea un espacio temporal privado. La contraseña se entrega al cliente MySQL mediante un archivo de opciones, evitando incluirla en los argumentos del proceso. Ese archivo se retira cuando finaliza la ejecución.&lt;br&gt;
La exportación se realiza con las siguientes opciones de mysqldump.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffkcpi75oob41ldlptuze.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Ffkcpi75oob41ldlptuze.png" alt=" " width="458" height="126"&gt;&lt;/a&gt;&lt;br&gt;
La opción --single-transaction obtiene una vista consistente de los datos en tablas InnoDB. Mientras se genera la copia deben evitarse cambios de estructura como ALTER, DROP y TRUNCATE. La exportación cubre la base de la aplicación, pero no incluye las cuentas del servidor, los procedimientos ni los eventos (Oracle, s. f.).&lt;br&gt;
Una vez validada la compresión, crypto.js obtiene la clave mediante scrypt y una sal aleatoria. AES 256 GCM protege el contenido con un nonce aleatorio y una etiqueta de autenticación. El archivo se identifica con la fecha y un valor aleatorio, y se acompaña de un checksum SHA 256. La frase de cifrado debe conservarse por separado mientras se necesiten las copias.&lt;br&gt;
El respaldo local puede ejecutarse desde PowerShell utilizando el contenedor de herramientas.&lt;br&gt;
docker compose --profile tools run --rm tools scripts/backup.sh&lt;br&gt;
Get-ChildItem backups&lt;br&gt;
El archivo .sql.gz.enc contiene datos comprimidos y cifrados; requiere el procedimiento de descifrado antes de leer el SQL. Debe conservarse junto a su archivo de checksum. La retención local elimina copias por antigüedad: siete días no significan necesariamente siete archivos, porque pueden realizarse varias ejecuciones en una misma fecha.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;6.Restauración y comprobación de los datos&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;El script restore.sh recupera la información en otra base para proteger el inventario activo. Primero exige que el destino sea diferente de DB_NAME y esté vacío. Después comprueba el checksum, valida el descifrado y revisa el archivo comprimido antes de importar el SQL.&lt;br&gt;
En el entorno de práctica, una cuenta administradora prepara la base de destino y otorga los permisos necesarios al usuario utilizado por el proyecto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F395z5svienxwiomfnvcy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F395z5svienxwiomfnvcy.png" alt=" " width="713" height="283"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La prueba local reportada se ejecutó con MySQL 8.0.46. Se añadió un cuarto producto y se generó el respaldo. Al restaurarlo, se verificaron las cuatro filas y la igualdad de sus campos. Después se eliminó el registro de prueba desde la API y se comprobó que continuaba disponible en la base recuperada.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fraox1t2e4o1z4ygwj69v.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fraox1t2e4o1z4ygwj69v.png" alt=" " width="697" height="285"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;También se verificó que el procedimiento rechazara una copia alterada, un destino con tablas y un destino con el mismo nombre de la base activa. La importación completa no constituye una operación atómica: ante un fallo parcial, hay que revisar los objetos creados antes de repetirla.&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;7.Conexión de MySQL y publicación en Railway&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Para utilizar MySQL en Aiven se toman de su consola el host, el puerto, el usuario, la contraseña y la base, además del certificado CA. Deben emplearse los valores reales del servicio: la base puede ser defaultdb y el puerto puede diferir de 3306. Las guías de conexión explican estos parámetros y el uso del certificado (Aiven, s. f.-a, s. f.-b).&lt;br&gt;
Railway ejecuta el servicio Node.js a partir del Dockerfile. Las variables de entorno indican cómo conectarse a MySQL. El archivo railway.json ejecuta la inicialización antes del despliegue y utiliza /health para revisar el servicio. La inicialización está preparada para conservar una tabla que ya exista.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvsxfk48exon834xwgtvj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fvsxfk48exon834xwgtvj.png" alt=" " width="685" height="425"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La aplicación publicada dispone de un dominio de Railway. En la comprobación realizada, la página devolvió HTTP 200 y /health respondió con status ok y database MySQL. El servicio escucha en 0.0.0.0 y toma el puerto del entorno. Esta respuesta confirma conectividad con MySQL, pero por sí sola no identifica su proveedor.&lt;br&gt;
El runner de GitHub Actions necesita acceso de red a la base remota. Si se restringen las direcciones permitidas, debe habilitarse la salida del runner o utilizar uno autorizado. Los valores sensibles se almacenan en Secrets y la conexión externa mantiene la verificación TLS.&lt;/p&gt;

&lt;p&gt;**&lt;br&gt;
8.Validación del código y publicación automática**&lt;/p&gt;

&lt;p&gt;La automatización distingue las pruebas del código, el despliegue y la copia de datos. El archivo ci.yml define pruebas de API y cifrado y una recuperación con MySQL 8.4. El flujo deploy.yml espera el resultado de CI en main para publicar el commit que superó las comprobaciones.&lt;br&gt;
El flujo de despliegue recibe la finalización de CI mediante este evento.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsyd52tu8uc5wt2l44zkg.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fsyd52tu8uc5wt2l44zkg.png" alt=" " width="499" height="115"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;El despliegue exige una ejecución de CI exitosa originada por un push del propio repositorio y la variable DEPLOY_ENABLED=true. Utiliza RAILWAY_TOKEN y RAILWAY_SERVICE_ID para seleccionar el servicio. Railway describe los Project Tokens para esta automatización (Railway, s. f.-a).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc5g2i7t30zbxjfuiztus.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fc5g2i7t30zbxjfuiztus.png" alt=" " width="296" height="43"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Después de railway up se consulta /health en el dominio público. Para comprobar el flujo completo puede modificarse un texto visible, enviar el commit a main y revisar CI, el despliegue y el cambio publicado. Conviene elegir un único mecanismo de publicación para evitar ejecuciones duplicadas. La documentación de railway up describe su comportamiento y opciones (Railway, s. f.-b).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;9. Copias remotas mediante GitHub Actions&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El flujo backup.yml construye un contenedor con el cliente MySQL 8.4 y llama al mismo script usado en las pruebas locales. Las credenciales de la base y la frase de cifrado se obtienen de Secrets. El artefacto conserva el resultado cifrado y su checksum.&lt;br&gt;
Estas definiciones establecen el horario previsto y la conservación del artefacto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkfhb82qjm2xsakgxv9bn.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkfhb82qjm2xsakgxv9bn.png" alt=" " width="363" height="265"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;La ejecución requiere BACKUP_ENABLED=true. Se configuran DB_HOST, DB_PORT, DB_USER, DB_PASSWORD, DB_NAME, DB_SSL_CA y BACKUP_PASSPHRASE como Secrets. Para Aiven, BACKUP_DB_SSL debe estar en true. La cuenta de respaldo necesita acceso de lectura y los permisos de los objetos incluidos en la exportación.&lt;br&gt;
La captura incorporada muestra una ejecución iniciada con workflow_dispatch que terminó correctamente y produjo un artefacto descargable. Este resultado acredita una prueba manual. Para verificar el horario diario hay que revisar una ejecución identificada como Scheduled. También debe descargarse la copia y comprobar su restauración en una base vacía usando la misma frase de cifrado.&lt;br&gt;
Los artefactos se descargan desde la página de la ejecución en GitHub Actions; git clone solo obtiene los archivos versionados del repositorio (GitHub, s. f.).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa0zqp1p8oma14kedr9pj.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fa0zqp1p8oma14kedr9pj.png" alt=" " width="711" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;*&lt;em&gt;10. Resultados y evidencias del proyecto&lt;br&gt;
*&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;La Figura 5 documenta el registro de un producto en el inventario local. Esta prueba permite observar la validación y la actualización de la lista, pero no demuestra por sí misma una recuperación ni un despliegue remoto.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5fpx2p9xb361cl3wvw5e.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F5fpx2p9xb361cl3wvw5e.png" alt=" " width="694" height="678"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Las pruebas de recuperación reportadas corresponden a MySQL 8.0.46. El entorno de Docker y los workflows emplean MySQL 8.4. Las nuevas capturas documentan el repositorio público y un backup manual exitoso. La respuesta de /health confirma que la aplicación pública llega a MySQL. Aún debe documentarse la ejecución programada y la restauración del respaldo remoto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Conclusiones&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;El ejercicio de restauración permitió recuperar un estado anterior del inventario y comparar sus registros. Trabajar con una base de destino independiente evitó sobrescribir los datos activos durante la validación.&lt;br&gt;
La propuesta integra la comprobación del software, su publicación y la protección de sus datos en flujos separados. Railway mantiene accesible la API; la configuración permite conectar MySQL en Aiven, y GitHub Actions ejecuta las tareas definidas en el repositorio.&lt;br&gt;
Para una demostración con pocos datos, la exportación completa ofrece un procedimiento fácil de revisar. Un uso en producción requeriría definir el RPO y la retención según el negocio, añadir alertas y ensayos de recuperación y guardar otra copia independiente. El proyecto no implementa por sí solo una estrategia completa 3 2 1 ni recuperación PITR.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Enlaces de la entrega
Repositorio público del proyecto: &lt;a href="https://github.com/Soanny/mysql-backup-strategy_Stevie-Marca_Soanny-Rivas" rel="noopener noreferrer"&gt;https://github.com/Soanny/mysql-backup-strategy_Stevie-Marca_Soanny-Rivas&lt;/a&gt;
Aplicación pública en Railway:  &lt;a href="https://mysql-backup-strategystevie-marcasoanny-rivas-production.up.railway.app/" rel="noopener noreferrer"&gt;https://mysql-backup-strategystevie-marcasoanny-rivas-production.up.railway.app/&lt;/a&gt;
Video público de máximo cinco minutos:  &lt;a href="https://www.youtube.com/watch?v=ViPQl8gZ20U" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=ViPQl8gZ20U&lt;/a&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Referencias&lt;br&gt;
Aiven. (s. f.-a). Connect to Aiven for MySQL® from the command line. Recuperado el 3 de octubre de 2026, de &lt;a href="https://aiven.io/docs/products/mysql/howto/connect-from-clI" rel="noopener noreferrer"&gt;https://aiven.io/docs/products/mysql/howto/connect-from-clI&lt;/a&gt;&lt;br&gt;
Aiven. (s. f.-b). Connect to Aiven for MySQL® with MySQL Workbench. Recuperado el 3 de octubre de 2026, de &lt;a href="https://aiven.io/docs/products/mysql/howto/connect-from-mysql-workbench" rel="noopener noreferrer"&gt;https://aiven.io/docs/products/mysql/howto/connect-from-mysql-workbench&lt;/a&gt;&lt;br&gt;
GitHub. (s. f.). Downloading workflow artifacts. Recuperado el 3 de octubre de 2026, de &lt;a href="https://docs.github.com/en/actions/how-tos/manage-workflow-runs/download-workflow-artifacts" rel="noopener noreferrer"&gt;https://docs.github.com/en/actions/how-tos/manage-workflow-runs/download-workflow-artifacts&lt;/a&gt;&lt;br&gt;
Oracle. (s. f.). 6.5.4 mysqldump — A database backup program. Recuperado el 3 de octubre de 2026, de &lt;a href="https://dev.mysql.com/doc/refman/8.4/en/mysqldump.html" rel="noopener noreferrer"&gt;https://dev.mysql.com/doc/refman/8.4/en/mysqldump.html&lt;/a&gt;&lt;br&gt;
Railway. (s. f.-a). Deploying with the CLI. Recuperado el 3 de octubre de 2026, de &lt;a href="https://docs.railway.com/cli/deploying" rel="noopener noreferrer"&gt;https://docs.railway.com/cli/deploying&lt;/a&gt;&lt;br&gt;
Railway. (s. f.-b). railway up. Recuperado el 3 de octubre de 2026, de &lt;a href="https://docs.railway.com/cli/up" rel="noopener noreferrer"&gt;https://docs.railway.com/cli/up&lt;/a&gt;&lt;/p&gt;

</description>
      <category>automation</category>
      <category>database</category>
      <category>devops</category>
      <category>mysql</category>
    </item>
  </channel>
</rss>
