#!/usr/bin/env bash
# validate-paste.sh -- pre-flight a bash script/paste block BEFORE running it.
#
# Mutates NOTHING. Checks a candidate script file for the failure modes that
# have actually bitten us:
# 1. bash -n parse errors (parse-only: passing this is NOT a correctness
# guarantee -- behavior-test separately)
# 2. CR bytes (Windows line endings) -- counted via python byte count,
# because grep for a CR literal false-positives on tokens containing $r
# 3. non-ASCII characters (silently corrupt some downstream consumers)
# 4. bare 'exit' outside a subshell wrapper -- kills the operator's login
# shell when pasted (WARN: heuristic, review hits by eye)
# 5. common destructive commands with no visible gate nearby (WARN only)
#
# Usage: bash validate-paste.sh <file.sh>
# Exit codes: 0 PROCEED, 1 HOLD (hard check failed), 2 usage/tool missing.
# ASCII + LF.
set -uo pipefail
IFS=$'\n\t'
if [ $# -ne 1 ] || [ ! -f "${1:-}" ]; then
echo "usage: validate-paste.sh <file.sh>" >&2
exit 2
fi
TARGET=$1
HOLD=0
# --- 1. parse check -----------------------------------------------------------
if bash -n "$TARGET" 2>/tmp/vp-parse.$$; then
echo "OK: bash -n parse (parse-only, not a correctness guarantee)"
else
echo "FAIL: bash -n parse errors:"
sed 's/^/ /' /tmp/vp-parse.$$
HOLD=1
fi
rm -f /tmp/vp-parse.$$
# --- 2. CR bytes (python byte count; grep on CR literals false-positives) ----
if command -v python3 >/dev/null 2>&1; then
CRS=$(python3 -c 'import sys; print(open(sys.argv[1],"rb").read().count(b"\x0d"))' "$TARGET")
if [ "$CRS" -eq 0 ]; then
echo "OK: no CR bytes (LF-only)"
else
echo "FAIL: $CRS CR byte(s) found -- Windows line endings; fix before use"
HOLD=1
fi
else
echo "WARN: python3 not found; CR check skipped"
fi
# --- 3. non-ASCII -------------------------------------------------------------
NONASCII=$(grep -nP '[^\x00-\x7F]' "$TARGET" || true)
if [ -z "$NONASCII" ]; then
echo "OK: ASCII-only"
else
echo "FAIL: non-ASCII characters found:"
printf '%s\n' "$NONASCII" | head -5 | sed 's/^/ /'
HOLD=1
fi
# --- 4. bare exit heuristic ---------------------------------------------------
# A paste block containing 'exit' at top level kills the operator's shell.
# Heuristic: flag files that use 'exit' but never open a subshell wrapper.
EXITS=$(grep -nE '(^|[;&| ])exit( |;|$)' "$TARGET" || true)
if [ -n "$EXITS" ] && ! grep -qE '^\(' "$TARGET"; then
echo "WARN: 'exit' present with no obvious subshell wrapper '( { ...; } )'"
echo " If this is a PASTE block, a bare exit kills the login shell:"
printf '%s\n' "$EXITS" | head -3 | sed 's/^/ /'
fi
# --- 5. destructive patterns --------------------------------------------------
DESTRUCTIVE=$(grep -nE '(^|[;&| ])(rm -rf|mkfs|dd if=|wipefs|sgdisk|parted) ' "$TARGET" || true)
if [ -n "$DESTRUCTIVE" ]; then
echo "WARN: destructive command(s) present -- confirm each is individually gated:"
printf '%s\n' "$DESTRUCTIVE" | head -5 | sed 's/^/ /'
fi
if [ "$HOLD" -eq 1 ]; then
echo "FAIL: HOLD -- fix the failures above before pasting/running"
exit 1
fi
echo "OK: PROCEED (remember: behavior-test; syntax checks are not enough)"