first commit
This commit is contained in:
Executable
+86
@@ -0,0 +1,86 @@
|
||||
#!/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
|
||||
Reference in New Issue
Block a user