87 lines
3.5 KiB
Bash
87 lines
3.5 KiB
Bash
#!/usr/bin/env bash
|
|||
|
|
# =============================================================================
|
||
|
|
# test-shell-safety.sh — jaga "jebakan set -e + grep" di skrip deploy
|
||
|
|
# -----------------------------------------------------------------------------
|
||
|
|
# JEBAKAN: pipeline yang memuat `grep` mengembalikan 1 saat TIDAK ada baris cocok.
|
||
|
|
# Di skrip ber-`set -euo pipefail`, hasil pipeline itu MEMATIKAN skrip DIAM-DIAM.
|
||
|
|
#
|
||
|
|
# Regresi nyata: e099b95 membuat `external_services()` di install.sh berakhir
|
||
|
|
# ... | grep -v '^$' | sort -u | paste ...
|
||
|
|
# sehingga `./install.sh` (single-host tanpa service eksternal) keluar exit 1
|
||
|
|
# TEPAT setelah banner. Perbaikan: `awk 'NF'` (buang baris kosong, selalu exit 0).
|
||
|
|
#
|
||
|
|
# YANG DIPERIKSA (hanya skrip ber-`set -e`):
|
||
|
|
# 1. `| grep -v` / `| grep -c` — mode FILTER data yang lazim exit 1 saat kosong,
|
||
|
|
# dan jarang dipakai sebagai uji boolean. Lain halnya `grep -q`/`-E` yang
|
||
|
|
# memang sering dipakai di `if`, sehingga TIDAK ditandai.
|
||
|
|
# 2. Idiom `grep -v '^$'` — pemicu langsung regresi di atas.
|
||
|
|
#
|
||
|
|
# Baik (1) maupun (2) DILEWATI bila barisnya jelas kondisional — `if/elif/while/
|
||
|
|
# until/!` atau memuat `&&`/`||` — karena status exit-nya memang dikonsumsi.
|
||
|
|
#
|
||
|
|
# Jalankan: ./scripts/test-shell-safety.sh
|
||
|
|
# =============================================================================
|
||
|
|
set -uo pipefail
|
||
|
|
|
||
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||
|
|
DEPLOY_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||
|
|
|
||
|
|
GRN=$'\e[32m'; RED=$'\e[31m'; BLD=$'\e[1m'; NC=$'\e[0m'
|
||
|
|
PASS_N=0; FAIL_N=0
|
||
|
|
ok() { printf " ${GRN}✅${NC} %s\n" "$1"; PASS_N=$((PASS_N+1)); }
|
||
|
|
bad() { printf " ${RED}❌${NC} %s\n" "$1"; FAIL_N=$((FAIL_N+1)); }
|
||
|
|
|
||
|
|
# Baris ini "aman" dari set -e karena status exit-nya dikonsumsi (kondisi operator)?
|
||
|
|
is_conditional() {
|
||
|
|
local s="$1"
|
||
|
|
case "${s}" in
|
||
|
|
if\ *|elif\ *|while\ *|until\ *|'!'*) return 0 ;;
|
||
|
|
esac
|
||
|
|
[[ "${s}" == *"&&"* || "${s}" == *"||"* || "${s}" == *"|| true"* ]] && return 0
|
||
|
|
return 1
|
||
|
|
}
|
||
|
|
|
||
|
|
echo -e "${BLD}Uji keamanan shell (jebakan set -e + grep)${NC}"
|
||
|
|
echo
|
||
|
|
|
||
|
|
checked=0
|
||
|
|
while IFS= read -r f; do
|
||
|
|
# Hanya skrip ber-`set -e` (opsi memuat huruf 'e'); `set -uo pipefail` tanpa -e
|
||
|
|
# tidak berisiko karena tak ada yang mematikan skrip.
|
||
|
|
grep -qE '^[[:space:]]*set -[a-zA-Z]*e' "$f" 2>/dev/null || continue
|
||
|
|
checked=$((checked+1))
|
||
|
|
rel="${f#"${DEPLOY_DIR}/"}"
|
||
|
|
file_bad=0
|
||
|
|
|
||
|
|
# (1) pipe ke grep -v / grep -c
|
||
|
|
while IFS=: read -r ln text; do
|
||
|
|
[[ -z "${ln}" ]] && continue
|
||
|
|
stripped="$(printf '%s' "${text}" | sed 's/^[[:space:]]*//')"
|
||
|
|
is_conditional "${stripped}" && continue
|
||
|
|
bad "${rel}:${ln} — pipe ke 'grep -v/-c' (exit 1 saat kosong → set -e mematikan)"
|
||
|
|
printf ' %s\n' "${stripped}"
|
||
|
|
file_bad=1
|
||
|
|
done < <(grep -nE '\|[[:space:]]*grep[[:space:]]+-[a-zA-Z]*[vc]' "$f" 2>/dev/null || true)
|
||
|
|
|
||
|
|
# (2) idiom terlarang grep -v '^$' (baris komentar & kondisional dikecualikan)
|
||
|
|
while IFS=: read -r ln text; do
|
||
|
|
[[ -z "${ln}" ]] && continue
|
||
|
|
stripped="$(printf '%s' "${text}" | sed 's/^[[:space:]]*//')"
|
||
|
|
[[ "${stripped}" == '#'* ]] && continue
|
||
|
|
is_conditional "${stripped}" && continue
|
||
|
|
bad "${rel}:${ln} — idiom terlarang grep -v '^\$' (pakai awk 'NF')"
|
||
|
|
file_bad=1
|
||
|
|
done < <(grep -nF "grep -v '^$'" "$f" 2>/dev/null || true)
|
||
|
|
|
||
|
|
[[ ${file_bad} -eq 0 ]] && ok "${rel}: bersih"
|
||
|
|
done < <(find "${DEPLOY_DIR}" -name '*.sh' -not -path '*/generated/*' | sort)
|
||
|
|
|
||
|
|
echo
|
||
|
|
echo " skrip ber-set -e diperiksa: ${checked}"
|
||
|
|
if [[ ${FAIL_N} -eq 0 ]]; then
|
||
|
|
echo -e " ${GRN}${BLD}SEMUA LULUS${NC} (${PASS_N} cek)"
|
||
|
|
exit 0
|
||
|
|
fi
|
||
|
|
echo -e " ${RED}${BLD}GAGAL${NC}: ${FAIL_N} masalah · ${PASS_N} lulus"
|
||
|
|
exit 1
|