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 --versionEjecutar 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,infoostyle - 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 -lInterpretar 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$FILEdentro de[ $FILE == '' ]y encat $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, ySC2070, 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
.shdel repositorio. - Ejecuta ShellCheck con
--severity=warningy 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./*.shen lugar de*.shpara 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 -- ./*.shUsar el formato de salida JSON para la automatización
ShellCheck admite varios formatos de salida mediante --format:
tty(predeterminado): salida de terminal legible para humanosjson: legible por máquinas; ideal para paneles, bloqueos personalizados o cargas en plataformas SASTgcc: 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=SC2086en 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=SC2016Configurar 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-conditionsyrequire-variable-braces)disable=SC2059: supresión para todo el proyecto en una excepción justificadaexternal-sources=true: sigue y comprueba las directivassource/.
# .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=SC2312Script 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' >&2Comprobació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,infoystyle— permite ajustar la barrera:--severity=warninges 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
.shellcheckrclo justifique. - Combine ShellCheck con
set -euo pipefail, entrecomillado explícito, terminadores-- argumenty 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
- Prevención de inyección de comandos y argumentos
- Gestión segura de secretos e higiene del entorno
- Ejecución con mínimos privilegios y disciplina con sudo
- Análisis estático y auditoría con ShellCheck