Mantener los metadatos fuera del código ejecutable

ShellBeginner
Practicar Ahora

Introducción

Un colaborador propone un cambio de código con título y descripción. Un trabajo de compilación transforma esos metadatos en un artefacto de vista previa. La integración continua (CI) consiste en ejecutar estos trabajos automáticamente cuando llegan cambios. Un ejecutor es el programa que realiza el trabajo. Aquí iniciarás tú mismo un pequeño ejecutor local preparado.

Todas las personas y eventos son sintéticos. El ejecutor usa Bash real en un contenedor desechable, sin red ni credenciales de despliegue. production en release.conf es un valor de prueba local: nunca despliega nada. El taller modela sustitución de texto y ejecución del shell, no los permisos ni disparadores de una plataforma alojada. La terminal CI Workshop es tu espacio de mantenimiento; Topology explica el flujo y muestra los canales reales al actualizarla.

Ejecutarás una compilación normal, rastrearás una inyección, editarás dos archivos breves y volverás a probar la conservación exacta de los metadatos. Basta con familiaridad básica con la terminal; el JSON y la sintaxis Bash necesarios se enseñan antes de usarlos.

Ejecutar una compilación normal

Selecciona la pestaña CI Workshop en la interfaz del laboratorio para comenzar este paso.

En este paso conocerás el resultado esperado antes de investigar: el título exacto debe convertirse en artefacto y el canal debe seguir siendo preview.

Usa la pestaña CI Workshop. pwd muestra el directorio actual; ls enumera sus archivos. El espacio es /home/labex/project/ci-workshop.

Cambia a la pestaña CI Workshop antes de ejecutar el siguiente comando.

pwd
ls

cat muestra un archivo de texto. Un objeto JSON usa llaves alrededor de campos con nombre; los dos puntos unen el nombre y su valor. Las cadenas JSON usan comillas dobles. Dentro de ellas, \n representa un salto de línea. Lee el evento normal:

cat events/ordinary.json

Su title es Improve search results. body es una descripción multilínea. Son datos aportados por un colaborador, por lo que la compilación no debe confiar en ellos como comandos.

Lee la pequeña definición del trabajo y su script:

cat job.json
cat build.sh
Elemento Significado en este ejecutor local
script Archivo Bash que se ejecuta, ahora build.sh
env Mapeo de nombres de entorno a cadenas; ahora vacío {}
{{event.title}} Sustituir este marcador exacto por el título antes de iniciar Bash
{{event.body}} La misma sustitución para la descripción
artifact.txt Archivo de metadatos generado
release.conf Canal inicializado a preview en cada ejecución

Bash lee instrucciones del shell. TITLE="..." asigna una variable; no pongas espacios alrededor de =. "$TITLE" expande la variable como un solo argumento y conserva espacios y saltos de línea. printf '%s' escribe una cadena sin añadir un salto; las comillas simples mantienen %s literal. > redirige la salida a un archivo reemplazando su contenido. El punto y coma separa comandos. set -euo pipefail detiene el script ante fallos habituales de comandos, variables sin definir y tuberías; no evita la inyección.

ci es el comando del ejecutor preparado. run recibe un nombre de evento sin .json y un nombre nuevo de directorio de salida. Cada ejecución comienza con una configuración preview nueva:

ci run ordinary first

Espera channel=preview y exit=0. El código cero indica que Bash terminó correctamente. Usa show para inspeccionar script generado, artefacto y configuración:

ci show first

El artefacto se muestra como cadena JSON, "Improve search results", para poder representar después saltos invisibles. Es contenido real del archivo, no un indicador simulado. La sección final debe mostrar preview. Cambia a la pestaña Topology y actualiza: la fila first lee ese mismo archivo.

Repetir run con first produce intencionadamente un error de directorio existente para conservar las pruebas anteriores. Elige un nombre nuevo como first-again. Las ejecuciones fallidas también conservan el script generado y build.log en runs/NAME para el diagnóstico.

Rastrear la sustitución peligrosa

Selecciona la pestaña CI Workshop en la interfaz del laboratorio para comenzar este paso.

En este paso observarás cómo los datos se convierten en código ejecutable y cambian el canal dentro de una demostración aislada.

El flujo es evento → trabajo → código shell generado → Bash → artefacto. El momento peligroso es la sustitución antes de que Bash analice las comillas. Las comillas dobles protegen una expansión dentro de un comando ya analizado; no protegen un valor insertado directamente en el código antes del análisis.

Lee el evento de demostración inocuo proporcionado:

Cambia a la pestaña CI Workshop antes de ejecutar el siguiente comando.

cat events/hostile.json

El title contiene comillas dobles, punto y coma, printf, otro punto y coma y #. En Bash, la comilla cierra la cadena original, el punto y coma inicia otro comando y # comenta el resto de la línea. Ese printf solo cambia el release.conf sintético de esta ejecución. El \" de JSON codifica una comilla; no la vuelve segura al insertarla después en código shell.

Ejecuta el evento en un espacio nuevo:

ci run hostile demo
ci show demo

Espera channel=production aunque el ejecutor inicializó preview. Localiza el printf insertado entre puntos y coma en generated.sh: Bash lo ejecutó realmente. El artefacto puede contener solo Update; el resto pasó a ser sintaxis en lugar de datos. Ese es el defecto que repararás.

demo está separado de first, y ninguno cambia job.json ni build.sh. Cambia a la pestaña Topology y actualiza la página para comparar sus canales reales. Una fila production indica un ejemplo modificado, nunca una publicación real. No ejecutes manualmente el texto del evento en tu shell de mantenimiento.

Topology compara los canales de la ejecución normal y de la demostración vulnerable

El riesgo general se documenta en la explicación de GitHub sobre inyecciones. Nuestros marcadores y formato son deliberadamente más pequeños que GitHub Actions; el mecanismo compartido es sustituir antes de analizar.

Pasar el valor como datos

Selecciona la pestaña CI Workshop en la interfaz del laboratorio para comenzar este paso.

En este paso mantendrás el texto fuera del código shell y conservarás espacios, comillas y varias líneas.

Una variable de entorno es una cadena con nombre entregada a un proceso nuevo. El mapeo env la establece directamente, sin generar una asignación shell. En JSON, un mapeo es otro objeto entre llaves. Asigna BUILD_TITLE desde {{event.title}} y deja que Bash expanda "$BUILD_TITLE" en ejecución. Bash no reinterpreta comillas, punto y coma, $() ni acentos graves dentro del valor expandido como comandos. No uses eval ni pases el valor a bash -c: lo analizarían otra vez.

Reemplazaremos dos archivos mediante documentos incrustados. cat > filename <<'JSON' escribe las líneas siguientes hasta una línea que contiene únicamente JSON. El delimitador entre comillas impide que el shell de mantenimiento expanda $ u otra sintaxis. Copia el bloque completo, incluido el delimitador final. Reemplaza todo el archivo y también permite recuperarse de errores de escritura.

Cambia a la pestaña CI Workshop antes de ejecutar el siguiente comando.

cat > job.json <<'JSON'
{
  "script": "build.sh",
  "env": {
    "BUILD_TITLE": "{{event.title}}"
  }
}
JSON

Ahora reemplaza el script. El delimitador BASH cumple la misma función literal. El script usa directamente la variable y no contiene un marcador del colaborador.

cat > build.sh <<'BASH'
set -euo pipefail
printf '%s' "$BUILD_TITLE" > artifact.txt
printf 'channel='; cat release.conf
BASH

Vuelve a leer ambos archivos con cat. Una coma o llave ausente causa un error JSON; un nombre de entorno mal escrito, un error de variable sin definir. Reemplaza el bloque correspondiente y reintenta. Una ejecución antigua refleja el script anterior, así que usa otro nombre:

ci run ordinary repaired
ci show repaired

El script generado contiene ahora "$BUILD_TITLE", no los caracteres del título. El artefacto sigue conteniendo Improve search results y el canal es preview.

Lee los ejemplos de puntuación y varias líneas antes de probar:

cat events/quotes.json
cat events/multiline.json

\n representa un salto real, y el último \n también debe conservarse. El apóstrofo de O'Brien y las comillas de un título son datos legítimos. El mismo mapeo pasa un body multilínea: la referencia cambia a {{event.body}}, pero se mantiene la regla de entrecomillar la variable. Reutilizarás este mecanismo para las notas de versión del desafío.

ci test normal ejecuta el trabajo y script enviados sin modificarlos con eventos normales, de puntuación y multilínea en directorios temporales desechables. Compara bytes exactos y comprueba preview. Borra solo esos directorios, conservando archivos enviados y ejecuciones guardadas.

ci test normal

Espera una línea exact metadata; channel=preview por ejemplo. Quitar puntuación, aplanar saltos, fijar un título, rechazar todos los eventos u omitir la compilación hace fallar esta prueba de funcionalidad. El error identifica el ejemplo; léelo y corrige tus dos archivos antes de reintentar.

Volver a probar el límite de la compilación

Selecciona la pestaña CI Workshop en la interfaz del laboratorio para comenzar este paso.

En este paso confirmarás que el mismo trabajo reparado conserva metadatos normales e impide el efecto demostrado.

Vuelve a ejecutar el ejemplo hostil con un nombre de salida nuevo:

Cambia a la pestaña CI Workshop antes de ejecutar el siguiente comando.

ci run hostile repaired-hostile
ci show repaired-hostile

El artefacto debe contener ahora el título hostil completo como texto. Los puntos y coma y las comillas aparecen dentro de la representación JSON, y release.conf permanece en preview. Las pruebas antiguas de demo no cambian porque pertenecen a otro espacio.

Otro ejemplo contiene $() y acentos graves, formas de sustitución de comandos cuando aparecen en código shell. Dentro de un valor de entorno expandido entre comillas dobles, siguen siendo datos. Lee el ejemplo:

cat events/substitution.json

Ejecuta los cinco con los mismos archivos enviados:

ci test all

Los casos ordinary, quotes, multiline, hostile y substitution deben conservar los metadatos exactos y preview, seguidos del mensaje de limpieza. Se comprueban ejecución real de Bash y archivos de salida, no una frase del registro ni un resultado inventado secure=true. La comprobación de finalización repite estas pruebas temporales sin modificar trabajo, script ni ejecuciones guardadas.

Si solo falla el caso hostil, busca marcadores de eventos restantes o un nuevo análisis de la variable. Si fallan puntuación o varias líneas, comprueba printf '%s' con una variable entre comillas dobles. No borres entradas problemáticas: conservar metadatos legítimos forma parte del objetivo. Cambia a la pestaña Topology y actualiza la página para ver repaired-hostile junto al demo histórico.

Topology muestra que la ejecución reparada conserva el canal preview

Resumen

Seguiste los metadatos del colaborador a través del ejecutor hasta Bash real, reprodujiste una modificación de configuración aislada y reparaste el límite con un mapeo de entorno y expansión entre comillas. Las pruebas exactas conservaron puntuación, varias líneas y canal de vista previa. Aplica ahora el mismo razonamiento a las notas de versión.