#!/bin/sh
# quickcheck.sh — zero-install PHP2Shell triage.
#
# Finds the PHP versions on this host's PATH and tells you whether each is
# affected by PHP2Shell (CVE-2026-109618 / CVE-2026-109619), using only POSIX
# shell and the fixed-version thresholds baked in below. No network, no deps.
#
# This is a first look for a sleepy admin at 3am. It only reads `php -v`; it
# does not inspect your configuration, dependencies, code, or whether running
# workers still hold the old binary. For that, run the full tool:
#
#     python3 shellproof.pyz scan
#
# Fixed releases (php.net, 2026-10-21): 8.5.12, 8.4.27, 8.3.36, 8.2.35.
# PHP 8.1 and older are end-of-life and receive no official PHP2Shell fix.

set -eu

# Fixed patch level per supported branch. Edit only to match php.net.
FIXED_85=12
FIXED_84=27
FIXED_83=36
FIXED_82=35

RED=''
GRN=''
YEL=''
BLD=''
RST=''
if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
	RED=$(printf '\033[31m')
	GRN=$(printf '\033[32m')
	YEL=$(printf '\033[33m')
	BLD=$(printf '\033[1m')
	RST=$(printf '\033[0m')
fi

found=0
affected=0
eol=0

# Print the verdict for one "major minor patch binary" tuple.
verdict() {
	major=$1
	minor=$2
	patch=$3
	bin=$4
	branch="${major}.${minor}"
	fixed=""

	if [ "$major" = "8" ] && [ "$minor" = "5" ]; then
		fixed=$FIXED_85
	elif [ "$major" = "8" ] && [ "$minor" = "4" ]; then
		fixed=$FIXED_84
	elif [ "$major" = "8" ] && [ "$minor" = "3" ]; then
		fixed=$FIXED_83
	elif [ "$major" = "8" ] && [ "$minor" = "2" ]; then
		fixed=$FIXED_82
	fi

	if [ -z "$fixed" ]; then
		# 8.1 and older: end of life, no official fix.
		eol=$((eol + 1))
		affected=$((affected + 1))
		printf '  %s%s%s  PHP %s.%s.%s  %sEND OF LIFE — no official PHP2Shell fix%s\n' \
			"$BLD" "$bin" "$RST" "$major" "$minor" "$patch" "$RED" "$RST"
		printf '      Branch %s is end-of-life. Migrate to a supported branch (8.4 or 8.5).\n' "$branch"
		return
	fi

	if [ "$patch" -lt "$fixed" ] 2>/dev/null; then
		affected=$((affected + 1))
		printf '  %s%s%s  PHP %s.%s.%s  %sAFFECTED%s — fixed in %s.%s\n' \
			"$BLD" "$bin" "$RST" "$major" "$minor" "$patch" "$RED" "$RST" "$branch" "$fixed"
		printf '      Upgrade to %s.%s or newer now, then restart every PHP worker.\n' "$branch" "$fixed"
	else
		printf '  %s%s%s  PHP %s.%s.%s  %sOK%s — at or above the fixed release (%s.%s)\n' \
			"$BLD" "$bin" "$RST" "$major" "$minor" "$patch" "$GRN" "$RST" "$branch" "$fixed"
	fi
}

# Collect candidate binary names: common PHP binaries on PATH.
candidates="php php8.5 php8.4 php8.3 php8.2 php8.1 php8.0 php7.4 php-cgi php-fpm"

seen_paths=""
check_bin() {
	name=$1
	path=$(command -v "$name" 2>/dev/null || true)
	[ -n "$path" ] || return 0
	# Dedupe by resolved path.
	case " $seen_paths " in
	*" $path "*) return 0 ;;
	esac
	seen_paths="$seen_paths $path"

	line=$("$name" -v 2>/dev/null | head -n 1 || true)
	# Extract the first MAJOR.MINOR.PATCH from the version line.
	ver=$(printf '%s\n' "$line" | sed -n 's/.*[Pp][Hh][Pp][^0-9]*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/p')
	[ -n "$ver" ] || ver=$(printf '%s\n' "$line" | sed -n 's/.*\([0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\).*/\1/p')
	if [ -z "$ver" ]; then
		printf '  %s%s%s  (could not read a version from %s -v)\n' "$BLD" "$name" "$RST" "$name"
		return 0
	fi
	found=$((found + 1))
	major=$(printf '%s\n' "$ver" | cut -d. -f1)
	minor=$(printf '%s\n' "$ver" | cut -d. -f2)
	patch=$(printf '%s\n' "$ver" | cut -d. -f3)
	verdict "$major" "$minor" "$patch" "$path"
}

printf '%sShellproof quickcheck%s — PHP2Shell (CVE-2026-109618 / CVE-2026-109619)\n' "$BLD" "$RST"
printf 'Scanning PHP binaries on PATH...\n\n'

for c in $candidates; do
	check_bin "$c"
done

printf '\n'
if [ "$found" -eq 0 ]; then
	printf '%sNo PHP binary found on PATH.%s A web server may still bundle its own PHP\n' "$YEL" "$RST"
	printf '(FPM, mod_php, LiteSpeed lsphp). Check the version your site reports and run the\n'
	printf 'full tool to inspect it: python3 shellproof.pyz scan\n'
	exit 0
fi

if [ "$affected" -gt 0 ]; then
	printf '%s%d of the PHP install(s) found need action.%s\n' "$RED" "$affected" "$RST"
	if [ "$eol" -gt 0 ]; then
		printf 'End-of-life branches cannot be patched in place; plan a migration.\n'
	fi
	printf 'Upgrading the binary is not enough on its own: workers that started before the\n'
	printf 'upgrade keep running the old code. Run the full tool for config, dependency,\n'
	printf 'webshell and stale-worker checks and exact per-platform fix steps:\n\n'
	printf '    %spython3 shellproof.pyz scan%s\n' "$BLD" "$RST"
	printf '    %spython3 shellproof.pyz fix%s\n' "$BLD" "$RST"
	exit 1
fi

printf '%sEvery PHP binary on PATH is at or above its fixed release.%s\n' "$GRN" "$RST"
printf 'Still confirm the version your web server runs and that all workers were\n'
printf 'restarted after the upgrade:\n\n'
printf '    %spython3 shellproof.pyz scan%s\n' "$BLD" "$RST"
exit 0
