Análise estática e auditoria com ShellCheck
Integre o ShellCheck a uma barreira de segurança e interprete suas descobertas para reforçar cada script.
Análise estática e auditoria com ShellCheck é uma aula grátis de DevOps Bootcamp no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de DevOps Bootcamp, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de DevOps Bootcamp inclui 4 aulas no total.
O que é ShellCheck e por que ele importa
ShellCheck é uma ferramenta de análise estática de código aberto para scripts de shell. Ela analisa seu código-fonte Bash (e POSIX sh, dash, ksh) sem executá-lo e relata bugs, construções inseguras, problemas de portabilidade e problemas de estilo — cada um identificado por um código de regra exclusivo, como SC2086.
Em um pipeline reforçado por segurança, o ShellCheck atua como um gate obrigatório: nenhum script é entregue até ser aprovado. Isso é importante porque:
- Muitas vulnerabilidades de shell (separação de palavras, injeção e expansão sem aspas) ficam invisíveis durante os testes do caminho esperado, mas são acionadas quando a entrada é controlada por um invasor.
- O ShellCheck detecta essas classes de bugs antes da execução, sem custo.
- Ele documenta por que cada padrão é perigoso, tornando sua equipe mais consciente com o tempo.
Instale-o em qualquer 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 --versionExecutando o ShellCheck pela Primeira Vez
A chamada mais simples é shellcheck <script>. O ShellCheck lê a linha shebang para determinar o dialeto do shell e, em seguida, emite as descobertas na saída padrão.
Cada descoberta inclui:
- Arquivo e número da linha — localização exata
- Gravidade —
error,warning,infooustyle - Código SC — identificador estável da regra, que você pode consultar ou suprimir
- Explicação em linguagem clara — informa o que está errado e, muitas vezes, como corrigir
Execute o script abaixo e observe a saída que o ShellCheck produziria:
#!/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 -lInterpretando a Saída do ShellCheck e os Códigos SC
Para o script da cena anterior, o ShellCheck emitiria descobertas como:
SC2086(aviso) — Use aspas duplas para evitar expansão de curingas e separação de palavras — em$FILEdentro de[ $FILE == '' ]e emcat $FILE.SC2039/SC3010(informação) — == em [ ] é uma particularidade do bash; use = para POSIX.SC2002(estilo) — cat inútil. Considere cmd < file em vez de cat file | cmd.
Cada código SC corresponde a uma página wiki em https://www.shellcheck.net/wiki/SCxxxx, com justificativa e um exemplo corrigido.
A versão corrigida desse 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"Níveis de Gravidade e O que Exige Ação
O ShellCheck classifica cada descoberta por gravidade. Em um gate de segurança, você deve tratá-las da seguinte forma:
- error — Quase certamente um bug ou uma falha de segurança. Bloqueie a compilação. Corrija imediatamente. Exemplo:
SC2148, shebang ausente;SC2070,$?sem aspas. - warning — Padrão de alto risco, frequentemente explorável. Bloqueie a compilação. Corrija ou justifique explicitamente a supressão. Exemplo:
SC2086, variável sem aspas. - info — Provavelmente correto hoje, mas frágil ou não portável. Corrija no mesmo PR, a menos que seja considerado fora do escopo.
- style — Preferência estética ou do POSIX. Recomendado, mas opcional em uma base de código exclusivamente Bash.
Use --severity=warning para sair com código diferente de zero somente em avisos ou níveis superiores — o limite padrão de um gate de segurança:
#!/usr/bin/env bash
# gate.sh — fail CI on errors and warnings only
shellcheck --severity=warning scripts/*.sh
echo "ShellCheck exit code: $?"Integrando o ShellCheck como Gate de Segurança da CI
Um gate de segurança só é útil quando é obrigatório e automatizado. O padrão abaixo envolve o ShellCheck em uma etapa da CI que:
- Encontra cada arquivo
.shno repositório. - Executa o ShellCheck com
--severity=warninge saída JSON legível por máquina. - Faz o pipeline falhar (
exit 1) se existir qualquer descoberta. - Imprime um resumo para que os engenheiros possam agir sobre as descobertas sem sair do registro da CI.
Coloque este arquivo no seu repositório e chame-o do seu pipeline 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."A Família SC2086: Expansões de Variáveis sem Aspas
SC2086 é a descoberta mais comum do ShellCheck e uma das vulnerabilidades de shell mais exploradas: expansões de variáveis sem aspas.
Quando uma variável não está entre aspas duplas, o shell executa a separação de palavras (divide em IFS) e a expansão de curingas sobre seu valor. Um invasor que controle a variável pode injetar argumentos extras, provocar travessia do sistema de arquivos ou fazer com que os comandos recebam operandos inesperados.
Padrão clássico perigoso:
#!/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[@]}"Detectando o Risco de Injeção de Comandos com SC2046 e SC2035
Duas regras menos conhecidas, mas críticas, tratam da injeção de comandos por meio da saída de subshells:
SC2046— Coloque isto entre aspas para evitar separação de palavras / curingas dentro de$(…). Se a saída de um subshell for usada sem aspas, qualquer espaço em branco ou caractere curinga na saída se tornará um token do shell.SC2035— Use./*.shem vez de*.shpara evitar que nomes de arquivos iniciados por-sejam interpretados como opções (um vetor clássico de injeção de argumentos).
Cenário concreto de exploração e correção:
#!/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 -- ./*.shUsando o Formato de Saída JSON para Automação
O ShellCheck oferece suporte a vários formatos de saída por meio de --format:
tty(padrão) — saída de terminal legível por humanosjson— legível por máquina; ideal para painéis, bloqueadores personalizados ou envio para plataformas SASTgcc— compatível com ferramentas que analisam o formato de erros do GCC (IDEs, Vim/Emacs)checkstyle— formato XML consumido pelo plug-in Checkstyle do Jenkins
O formato JSON permite escrever políticas automatizadas, por exemplo, bloqueando somente códigos SC específicos ou agregando descobertas de uma grande base de código em um relatório de segurança.
#!/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)'Suprimindo Falsos Positivos Corretamente
Desativar o ShellCheck indiscriminadamente anula seu propósito. A abordagem correta é uma supressão direcionada e documentada, que afete somente a linha ou o bloco exato em que a descoberta realmente não se aplica.
Três mecanismos de supressão:
- Desativação em linha —
# shellcheck disable=SC2086na linha acima do código problemático. Afeta somente essa linha. - Desativação/ativação de bloco — envolva uma seção com
# shellcheck disable=…e# shellcheck enable=…. - Diretiva no nível do arquivo — coloque
# shellcheck disable=…no início do arquivo (raramente justificável; documente o motivo).
Toda supressão deve incluir um comentário explicando por que a descoberta é um 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=SC2016Configurando o ShellCheck por meio de .shellcheckrc
Para configurações que abrangem todo o projeto, o ShellCheck lê .shellcheckrc do diretório do script, subindo até /. Isso permite evitar a repetição de sinalizadores em cada chamada e mantém os scripts do gate simples.
Diretivas úteis em .shellcheckrc:
shell=bash— substitui a detecção do dialeto (útil para arquivos sem shebang)enable=all— ativa verificações opcionais (por exemplo,avoid-nullary-conditions,require-variable-braces)disable=SC2059— supressão para todo o projeto em uma exceção justificadaexternal-sources=true— segue e verifica diretivassource/.
# .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 Reforçado de Ponta a Ponta: Antes e Depois
A maneira mais eficaz de assimilar as descobertas do ShellCheck é refatorar um script realista, passando de um estado reprovado para um estado limpo e reforçado. O script abaixo faz backup de um diretório e foi escrito sem levar a segurança em consideração. Ele falha no ShellCheck em pelo menos cinco regras distintas.
Estude as duas versões. A versão depois passa por shellcheck --severity=warning sem diretivas de supressão e é significativamente mais segura diante de entradas controladas por um invasor:
#!/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' >&2Verificação de Conhecimento: ShellCheck em um Gate de Segurança
Teste sua compreensão sobre o papel do ShellCheck como gate de segurança.
Recapitulação: Análise Estática como Gate de Segurança
Nesta lição, você aprendeu a tornar o ShellCheck um gate de segurança obrigatório no seu fluxo de trabalho Bash:
- O ShellCheck realiza análise estática sem executar seus scripts, detectando erros de uso de aspas, riscos de injeção e padrões inseguros antes da execução.
- Cada descoberta contém um código SC (por exemplo,
SC2086) vinculado a uma documentação detalhada e orientações para correção. - A escala de gravidade —
error,warning,info,style— permite ajustar o gate:--severity=warningé o limite de segurança recomendado. - Use saída legível por máquina (
--format=json) para automatizar relatórios, acompanhar tendências e integrar com SAST. - Suprima com moderação: sempre direcione a uma única linha, sempre documente o motivo em um comentário e nunca suprima globalmente, a menos que isso seja justificado por
.shellcheckrc. - Combine o ShellCheck com
set -euo pipefail, uso explícito de aspas, terminadores-- argumente validação de entradas para obter defesa em profundidade.
Um script aprovado pelo ShellCheck não é automaticamente seguro — mas um script reprovado pelo ShellCheck nunca deve chegar à produção.
Perguntas Frequentes
A aula “Análise estática e auditoria com ShellCheck” é grátis?
Sim — o texto completo de “Análise estática e auditoria com ShellCheck” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de DevOps Bootcamp, atualize para CoddyKit PRO. O curso de DevOps Bootcamp inclui 4 aulas no total.
O que vou aprender em “Análise estática e auditoria com ShellCheck”?
Integre o ShellCheck a uma barreira de segurança e interprete suas descobertas para reforçar cada script. Você pratica DevOps Bootcamp com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar DevOps Bootcamp?
Nenhuma experiência prévia é necessária. DevOps Bootcamp no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.
Quanto tempo leva a aula “Análise estática e auditoria com ShellCheck”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de DevOps Bootcamp?
Sim. Cada aula de DevOps Bootcamp inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Prevenção de injeção de comandos e argumentos
- Tratamento seguro de segredos e higiene do ambiente
- Execução com privilégios mínimos e disciplina no sudo
- Análise estática e auditoria com ShellCheck