123 lines
3.0 KiB
Bash
123 lines
3.0 KiB
Bash
#!/usr/bin/env bash
|
|
# verify-protection.sh — confirm a deployed Arising Media site is not leaking
|
|
# build artifacts, server config, or dotfiles to the public web.
|
|
#
|
|
# Usage:
|
|
# ./verify-protection.sh <base-url>
|
|
# ./verify-protection.sh http://localhost:8010
|
|
# ./verify-protection.sh https://cobhamtech.com
|
|
#
|
|
# Exit 0 if every check passes, 1 otherwise. Designed to be run after every
|
|
# deploy and in CI.
|
|
|
|
set -euo pipefail
|
|
|
|
BASE="${1:-}"
|
|
if [[ -z "$BASE" ]]; then
|
|
echo "usage: $0 <base-url>" >&2
|
|
exit 2
|
|
fi
|
|
BASE="${BASE%/}"
|
|
|
|
# Required paths — site must serve these; missing = audit FAIL.
|
|
REQUIRED=(
|
|
"/"
|
|
)
|
|
|
|
# Public paths — should be reachable; missing = WARN (content gap, not a leak).
|
|
PUBLIC=(
|
|
"/robots.txt"
|
|
"/sitemap.xml"
|
|
)
|
|
|
|
# Sensitive paths — must NOT return 200. 404 or 403 is acceptable.
|
|
# This list mirrors the deny patterns in SOP 08 nginx.conf.
|
|
SENSITIVE=(
|
|
"/Dockerfile"
|
|
"/dockerfile"
|
|
"/docker-compose.yml"
|
|
"/nginx.conf"
|
|
"/.dockerignore"
|
|
"/.gitignore"
|
|
"/.git/config"
|
|
"/.git/HEAD"
|
|
"/.env"
|
|
"/.env.example"
|
|
"/api/.env"
|
|
"/.planning/"
|
|
"/.planning/build_locations.py"
|
|
"/.planning/build_services.py"
|
|
"/.planning/regen_images.py"
|
|
"/.planning/playwright_audit.py"
|
|
"/__pycache__/"
|
|
"/build_locations.py"
|
|
"/build_services.py"
|
|
"/regen_images.py"
|
|
"/playwright_audit.py"
|
|
"/server.py"
|
|
"/main.py"
|
|
"/README.md"
|
|
"/package.json"
|
|
"/composer.json"
|
|
)
|
|
|
|
fail=0
|
|
|
|
warn=0
|
|
|
|
probe() {
|
|
local path="$1"
|
|
local expect="$2" # public | required | sensitive
|
|
local code
|
|
code=$(curl -k -s -o /dev/null -w '%{http_code}' --max-time 5 "${BASE}${path}" || echo "000")
|
|
case "$expect" in
|
|
required)
|
|
if [[ "$code" =~ ^(200|301|302|304)$ ]]; then
|
|
printf ' OK %-3s %s\n' "$code" "$path"
|
|
else
|
|
printf ' FAIL %-3s %s (required public path unreachable)\n' "$code" "$path"
|
|
fail=1
|
|
fi
|
|
;;
|
|
public)
|
|
if [[ "$code" =~ ^(200|301|302|304)$ ]]; then
|
|
printf ' OK %-3s %s\n' "$code" "$path"
|
|
else
|
|
printf ' WARN %-3s %s (public path unreachable — content gap, not a leak)\n' "$code" "$path"
|
|
warn=1
|
|
fi
|
|
;;
|
|
sensitive)
|
|
if [[ "$code" == "200" ]]; then
|
|
printf ' LEAK %-3s %s (must not return 200)\n' "$code" "$path"
|
|
fail=1
|
|
else
|
|
printf ' OK %-3s %s\n' "$code" "$path"
|
|
fi
|
|
;;
|
|
esac
|
|
}
|
|
|
|
echo "Verifying ${BASE}"
|
|
echo
|
|
echo "Required paths (site must serve these):"
|
|
for p in "${REQUIRED[@]}"; do probe "$p" required; done
|
|
echo
|
|
echo "Public paths (should be reachable):"
|
|
for p in "${PUBLIC[@]}"; do probe "$p" public; done
|
|
echo
|
|
echo "Sensitive paths (must not be reachable):"
|
|
for p in "${SENSITIVE[@]}"; do probe "$p" sensitive; done
|
|
echo
|
|
|
|
if [[ $fail -ne 0 ]]; then
|
|
echo "FAIL — exposure or required-path failure at ${BASE}" >&2
|
|
exit 1
|
|
elif [[ $warn -ne 0 ]]; then
|
|
echo "PASS (with warnings) — no exposure at ${BASE}, but missing public content"
|
|
exit 0
|
|
else
|
|
echo "PASS — no exposure detected at ${BASE}"
|
|
exit 0
|
|
fi
|