0Pricing
Linux Command Line & Bash Scripting Mastery · Lección

Análisis estático y auditoría con ShellCheck

Integre ShellCheck en una barrera de seguridad e interprete sus resultados para reforzar cada script.

Análisis estático y auditoría con ShellCheck es una lección gratuita de Linux Command Line & Bash Scripting Mastery en CoddyKit. Esta es la lección 4 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Linux Command Line & Bash Scripting Mastery, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.

Qué es ShellCheck y por qué es importante

ShellCheck es una herramienta de análisis estático de código abierto para scripts de shell. Analiza el código fuente de Bash (y de POSIX sh, dash y ksh) sin ejecutarlo e informa de errores, construcciones inseguras, problemas de portabilidad y problemas de estilo; cada uno lleva una etiqueta con un código de regla único, como SC2086.

En un flujo reforzado de seguridad, ShellCheck actúa como una barrera obligatoria: no se publica ningún script hasta que la supera. Esto es importante porque:

  • Muchas vulnerabilidades del shell (división de palabras, inyección y expansiones sin entrecomillar) son invisibles durante las pruebas del flujo correcto, pero se activan con entradas controladas por un atacante.
  • ShellCheck detecta estas clases de errores antes del tiempo de ejecución y sin coste.
  • Documenta por qué cada patrón es peligroso, lo que aumenta con el tiempo la concienciación del equipo.

Instálelo en cualquier sistema:

# Debian / Ubuntu
sudo apt-get install shellcheck

# macOS (Homebrew)
brew install shellcheck

# From source via Cabal (any platform)
cabal update && cabal install ShellCheck

# Verify
shellcheck --version

Ejecutar ShellCheck por primera vez

La invocación más sencilla es shellcheck <script>. ShellCheck lee la línea shebang para determinar el dialecto del shell y, a continuación, muestra los resultados en stdout.

Cada resultado incluye:

  • Archivo y número de línea: ubicación exacta
  • Gravedad: error, warning, info o style
  • Código SC: identificador estable de la regla que puede consultar o suprimir
  • Explicación para humanos: indica qué está mal y, a menudo, cómo corregirlo

Ejecute el script siguiente y observe el resultado que produciría ShellCheck:

#!/usr/bin/env bash
# demo_bad.sh — intentionally flawed for ShellCheck demonstration

FILE=$1

if [ $FILE == '' ]; then
  echo "No file given"
fi

cat $FILE | grep 'error' | wc -l

Interpretar los resultados y códigos SC de ShellCheck

Para el script de la escena anterior, ShellCheck mostraría resultados como los siguientes:

  • SC2086 (warning): Entrecomille con comillas dobles para evitar la expansión de comodines y la división de palabras, en $FILE dentro de [ $FILE == '' ] y en cat $FILE.
  • SC2039 / SC3010 (info): == en [ ] es un bashismo; use = para POSIX.
  • SC2002 (style): Cat innecesario. Considere cmd < file en lugar de cat file | cmd.

Cada código SC corresponde a una página wiki en https://www.shellcheck.net/wiki/SCxxxx, con la justificación y un ejemplo corregido.

La versión corregida de ese script:

#!/usr/bin/env bash
# demo_fixed.sh — ShellCheck-clean

FILE="$1"

if [ -z "$FILE" ]; then
  echo "No file given" >&2
  exit 1
fi

grep -c 'error' "$FILE"

Niveles de gravedad y acciones recomendadas

ShellCheck clasifica cada resultado según su gravedad. En una barrera de seguridad, debe tratarlos de la siguiente manera:

  • error: casi con toda seguridad es un error o un agujero de seguridad. Bloquee la compilación. Corríjalo inmediatamente. Ejemplos: SC2148, que indica que falta el shebang, y SC2070, que indica un $? sin entrecomillar.
  • warning: patrón de alto riesgo que a menudo puede explotarse. Bloquee la compilación. Corríjalo o justifique explícitamente su supresión. Ejemplo: variable sin entrecomillar como SC2086.
  • info: probablemente sea correcto hoy, pero es frágil o no portable. Corríjalo en la misma solicitud de cambios, salvo que se haya excluido explícitamente del alcance.
  • style: preferencia estética o de POSIX. Se recomienda, pero es opcional en una base de código exclusivamente de Bash.

Use --severity=warning para obtener un código de salida distinto de cero únicamente con advertencias o resultados de mayor gravedad: es el umbral estándar de una barrera de seguridad:

#!/usr/bin/env bash
# gate.sh — fail CI on errors and warnings only
shellcheck --severity=warning scripts/*.sh
echo "ShellCheck exit code: $?"

Integrar ShellCheck como barrera de seguridad de CI

Una barrera de seguridad solo es útil cuando es obligatoria y automatizada. El patrón siguiente integra ShellCheck en un paso de CI que:

  • Encuentra todos los archivos .sh del repositorio.
  • Ejecuta ShellCheck con --severity=warning y resultados JSON legibles por máquinas.
  • Falla la canalización (exit 1) si existe algún resultado.
  • Imprime un resumen para que los ingenieros puedan actuar sobre los resultados sin salir del registro de CI.

Coloque este archivo en su repositorio y llámelo desde su canalización de CI (GitHub Actions, Jenkins, GitLab CI, etc.):

#!/usr/bin/env bash
# ci/shellcheck_gate.sh
set -euo pipefail

SCRIPTS=$(find . -name '*.sh' -not -path './.git/*')
FAILED=0

for script in $SCRIPTS; do
  echo "==> Checking: $script"
  if ! shellcheck --severity=warning --format=tty "$script"; then
    FAILED=1
  fi
done

if [ "$FAILED" -eq 1 ]; then
  echo "[GATE] ShellCheck found warnings or errors. Build blocked." >&2
  exit 1
fi

echo "[GATE] All scripts passed ShellCheck."

La familia SC2086: expansiones de variables sin entrecomillar

SC2086 es el resultado más común de ShellCheck y una de las vulnerabilidades del shell más explotadas: las expansiones de variables sin entrecomillar.

Cuando una variable no está entrecomillada con comillas dobles, el shell realiza la división de palabras (divide según IFS) y la expansión de comodines sobre su valor. Un atacante que controle la variable puede inyectar argumentos adicionales, provocar recorridos no deseados del sistema de archivos o hacer que los comandos reciban operandos inesperados.

Patrón clásico peligroso:

#!/usr/bin/env bash
# Attacker sets: FILENAME="important.txt /etc/passwd"
FILENAME="$1"

# UNSAFE — word splitting turns this into two args
rm $FILENAME

# SAFE — double quotes prevent splitting
rm "$FILENAME"

# Arrays are the right tool for lists
FILES=("$@")
rm -- "${FILES[@]}"

Detectar riesgos de inyección de comandos con SC2046 y SC2035

Dos reglas menos conocidas, pero críticas, abordan la inyección de comandos mediante la salida de subcapas:

  • SC2046: Entrecomille esto para evitar la división de palabras y los comodines dentro de $(…). Si la salida de una subcapa se utiliza sin entrecomillar, cualquier espacio en blanco o carácter comodín de la salida se convierte en un token del shell.
  • SC2035: Use ./*.sh en lugar de *.sh para evitar que los nombres de archivo que comienzan por - se interpreten como opciones (un vector clásico de inyección de argumentos).

Escenario concreto de explotación y solución:

#!/usr/bin/env bash
# SC2046 example — output of find fed unquoted to chmod
# If a filename contains spaces, extra arguments appear

# UNSAFE
chmod 600 $(find /secrets -name '*.key')

# SAFE — use a while-read loop or xargs with -0
find /secrets -name '*.key' -print0 \
  | xargs -0 chmod 600

# SC2035 example
# UNSAFE — a file named '-rf' would be passed as an option
rm *.sh

# SAFE
rm -- ./*.sh

Usar el formato de salida JSON para la automatización

ShellCheck admite varios formatos de salida mediante --format:

  • tty (predeterminado): salida de terminal legible para humanos
  • json: legible por máquinas; ideal para paneles, bloqueos personalizados o cargas en plataformas SAST
  • gcc: compatible con herramientas que analizan el formato de errores de GCC (IDE, Vim/Emacs)
  • checkstyle: formato XML que consume el complemento Checkstyle de Jenkins

El formato JSON permite escribir políticas automatizadas, por ejemplo, bloquear únicamente determinados códigos SC o agregar resultados de una base de código grande en un informe de seguridad.

#!/usr/bin/env bash
# Emit JSON and filter for only error-severity findings using jq
shellcheck --format=json scripts/deploy.sh \
  | jq '[.[] | select(.level == "error")]'

# Count distinct SC codes across all scripts
find . -name '*.sh' -print0 \
  | xargs -0 shellcheck --format=json 2>/dev/null \
  | jq '[.[] | .code] | group_by(.) | map({code: .[0], count: length}) | sort_by(-.count)'

Suprimir correctamente los falsos positivos

Desactivar ShellCheck indiscriminadamente anula su propósito. El enfoque correcto es una supresión específica y documentada que afecte únicamente a la línea o el bloque exactos donde el resultado realmente no sea aplicable.

Hay tres mecanismos de supresión:

  • Desactivación en línea: # shellcheck disable=SC2086 en la línea anterior al código señalado. Solo afecta a esa línea.
  • Desactivación/activación de bloque: rodee una sección con # shellcheck disable=… y # shellcheck enable=….
  • Directiva a nivel de archivo: coloque # shellcheck disable=… al principio del archivo (rara vez está justificado; documente el motivo).

Toda supresión debe incluir un comentario que explique por qué el resultado es un falso positivo:

#!/usr/bin/env bash
# deploy.sh

# Legitimate suppression: $DEPLOY_ARGS is intentionally word-split
# because it is a pre-validated list of flags from a trusted config file.
# shellcheck disable=SC2086
exec deploy-tool $DEPLOY_ARGS

# Block suppression for a section that generates dynamic code
# shellcheck disable=SC2016
VARS='$HOME $PATH $USER'
echo "Unexpanded vars: $VARS"
# shellcheck enable=SC2016

Configurar ShellCheck mediante .shellcheckrc

Para la configuración de todo el proyecto, ShellCheck busca .shellcheckrc desde el directorio del script hacia arriba hasta /. Esto permite evitar la repetición de opciones en cada invocación y mantiene sencillos los scripts de la barrera.

Directivas útiles en .shellcheckrc:

  • shell=bash: sustituye la detección del dialecto (útil para archivos sin shebang)
  • enable=all: activa las comprobaciones opcionales (por ejemplo, avoid-nullary-conditions y require-variable-braces)
  • disable=SC2059: supresión para todo el proyecto en una excepción justificada
  • external-sources=true: sigue y comprueba las directivas source / .
# .shellcheckrc — project root
shell=bash
enable=all
external-sources=true

# SC2312: consider invoking this command separately to avoid masking its
# return value — suppressed project-wide because we use set -e.
# Rationale: errexit already aborts on failure; masking risk is mitigated.
disable=SC2312

Script reforzado de extremo a extremo: antes y después

La forma más eficaz de asimilar los resultados de ShellCheck es refactorizar un script realista, pasando de un estado con errores a otro limpio y reforzado. El script siguiente realiza una copia de seguridad de un directorio y se escribió sin tener en cuenta la seguridad. ShellCheck detecta en él al menos cinco reglas distintas.

Estudie ambas versiones. La versión posterior supera shellcheck --severity=warning sin directivas de supresión y es considerablemente más segura frente a entradas controladas por un atacante:

#!/usr/bin/env bash
# BEFORE — multiple ShellCheck violations
DEST=$1
SRC=$2
DATE=`date +%Y%m%d`

if [ ! -d $DEST ]; then
  mkdir $DEST
fi

cp -r $SRC $DEST/$DATE
echo Done


#!/usr/bin/env bash
# AFTER — ShellCheck-clean and hardened
set -euo pipefail

DEST="${1:?Usage: backup.sh <dest> <src>}"
SRC="${2:?Usage: backup.sh <dest> <src>}"
DATE=$(date +%Y%m%d)

if [ ! -d "$DEST" ]; then
  mkdir -p -- "$DEST"
fi

cp -r -- "$SRC" "$DEST/$DATE"
echo 'Done' >&2

Comprobación de conocimientos: ShellCheck en una barrera de seguridad

Compruebe su comprensión del papel de ShellCheck como barrera de seguridad.

Repaso: análisis estático como barrera de seguridad

En esta lección ha aprendido a convertir ShellCheck en una barrera de seguridad obligatoria en su flujo de trabajo de Bash:

  • ShellCheck realiza análisis estático sin ejecutar los scripts, y detecta errores de entrecomillado, riesgos de inyección y patrones inseguros antes del tiempo de ejecución.
  • Cada resultado incluye un código SC (por ejemplo, SC2086) enlazado con documentación detallada y orientación para corregirlo.
  • La escala de gravedad —error, warning, info y style— permite ajustar la barrera: --severity=warning es el umbral de seguridad recomendado.
  • Use resultados legibles por máquinas (--format=json) para automatizar informes, realizar un seguimiento de tendencias e integrar SAST.
  • Suprima con moderación: dirija siempre la supresión a una sola línea, documente siempre el motivo en un comentario y nunca suprima globalmente salvo que .shellcheckrc lo justifique.
  • Combine ShellCheck con set -euo pipefail, entrecomillado explícito, terminadores -- argument y validación de entradas para una defensa en profundidad.

Un script que supera ShellCheck no es automáticamente seguro, pero un script que no supera ShellCheck nunca debería llegar a producción.

Preguntas frecuentes

¿La lección «Análisis estático y auditoría con ShellCheck» es gratis?

Sí — el texto completo de «Análisis estático y auditoría con ShellCheck» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Linux Command Line & Bash Scripting Mastery, actualiza a CoddyKit PRO. El curso de Linux Command Line & Bash Scripting Mastery incluye 4 lecciones en total.

¿Qué aprenderé en «Análisis estático y auditoría con ShellCheck»?

Integre ShellCheck en una barrera de seguridad e interprete sus resultados para reforzar cada script. Practicas Linux Command Line & Bash Scripting Mastery con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.

¿Necesito experiencia previa para empezar Linux Command Line & Bash Scripting Mastery?

No se requiere experiencia previa. Linux Command Line & Bash Scripting Mastery en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 4 de 4.

¿Cuánto tiempo toma la lección «Análisis estático y auditoría con ShellCheck»?

La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.

¿Puedo escribir y ejecutar código en esta lección de Linux Command Line & Bash Scripting Mastery?

Sí. Cada lección de Linux Command Line & Bash Scripting Mastery incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.

Todas las lecciones de este curso

  1. Prevención de inyección de comandos y argumentos
  2. Gestión segura de secretos e higiene del entorno
  3. Ejecución con mínimos privilegios y disciplina con sudo
  4. Análisis estático y auditoría con ShellCheck
← Volver a Linux Command Line & Bash Scripting Mastery