#!/usr/bin/env bash
set -Eeuo pipefail

readonly stack_root="/srv/gitea-security"
readonly env_file="${ENV_FILE:-$stack_root/.env}"
readonly openappsec_root="$stack_root/openappsec"
readonly active_policy="$openappsec_root/local-policy/local_policy.yaml"
readonly prevent_policy="$openappsec_root/local-policy/prevent.yaml"
readonly generated_policy="$openappsec_root/config/policy.json"
timestamp="$(date -u +%Y%m%dT%H%M%SZ)"
readonly timestamp
readonly backup_dir="$stack_root/backups/prevent-$timestamp"
headers_file="$(mktemp)"
readonly headers_file
body_file="$(mktemp)"
readonly body_file
success=false
policy_changed=false
cpu_boosted=false

fail() {
  printf 'error=%s\n' "$1" >&2
  exit 1
}

strict_policy_active() {
  [[ -s "$generated_policy" ]] && jq -e '
    .waap.WAAP.WebApplicationSecurity[0].webAttackMitigationMode == "Prevent" and
    .waap.WAAP.WebApplicationSecurity[0].webAttackMitigationSeverity == "Medium" and
    .waap.WAAP.WebApplicationSecurity[0].webAttackMitigationAction == "High" and
    .waap.WAAP.WebApplicationSecurity[0].csrfProtection == "Prevent" and
    .waap.WAAP.WebApplicationSecurity[0].openRedirect == "Prevent" and
    .waap.WAAP.WebApplicationSecurity[0].errorDisclosure == "Prevent" and
    .triggers.rulebase.log[0].logToAgent == true
  ' "$generated_policy" >/dev/null
}

cleanup() {
  local exit_code=$?
  rm -f "$headers_file" "$body_file"
  if [[ "$success" != true && "$policy_changed" == true ]]; then
    install -m 0644 "$backup_dir/local_policy.yaml" "$active_policy"
    printf 'prevent=rolled-back backup=%s\n' "$backup_dir" >&2
  fi
  if [[ "$cpu_boosted" == true ]]; then
    docker update --cpus 2 openappsec-agent >/dev/null 2>&1 || true
  fi
  exit "$exit_code"
}
trap cleanup EXIT

[[ $(id -u) -eq 0 ]] || fail "run as root"
[[ -f "$env_file" ]] || fail "missing environment file: $env_file"
[[ -s "$prevent_policy" ]] || fail "missing prevent policy"
[[ "$(grep -c 'server 127.0.0.1:19080;' "$stack_root/openresty/conf.d/gitea.conf")" -eq 1 ]] || fail "run cutover.sh first"
set -a
# shellcheck source=/dev/null
source "$stack_root/VERSION.env"
# shellcheck source=/dev/null
source "$env_file"
set +a

install -d -m 0700 "$backup_dir"
install -m 0644 "$active_policy" "$backup_dir/local_policy.yaml"
actual_prevent_version="$(sha256sum "$prevent_policy" | awk '{ print $1 }')"
[[ "$actual_prevent_version" == "$OPENAPPSEC_PREVENT_POLICY_SHA256" ]] || fail "prevent policy checksum mismatch"
docker update --cpus 4 openappsec-agent >/dev/null
cpu_boosted=true
install -m 0644 "$prevent_policy" "$active_policy"
policy_changed=true

deadline=$((SECONDS + 240))
until strict_policy_active && [[ "$(jq -r '.version // empty' "$generated_policy")" == "$OPENAPPSEC_PREVENT_POLICY_SHA256" ]]; do
  (( SECONDS < deadline )) || fail "strict prevent policy did not become active"
  sleep 2
done
[[ "$(docker inspect --format '{{.State.Health.Status}}' openappsec-agent)" == healthy ]] || fail "OpenAppSec Agent is not healthy"

canary_url="https://$GIT_DOMAIN/__openappsec_verify?id=1%27%20OR%20%271%27%3D%271"
status_code="$(curl --insecure --silent --show-error --resolve "$GIT_DOMAIN:443:127.0.0.1" -D "$headers_file" -o "$body_file" -w '%{http_code}' "$canary_url")"
[[ "$status_code" == 403 ]] || fail "SQLi canary returned $status_code"
grep -Eiq '^X-Event-ID:[[:space:]]*[^[:space:]]+' "$headers_file" || fail "SQLi response has no X-Event-ID"
[[ "$(wc -c < "$body_file" | tr -d ' ')" == 10 ]] || fail "filtered block body is not 10 bytes"
[[ "$(cat "$body_file")" == Forbidden ]] || fail "unexpected filtered block body"
if grep -Ei '^Content-Encoding:' "$headers_file" >/dev/null; then
  fail "filtered response retained upstream content encoding"
fi
transfer_encoding="$(awk -F ': ' 'tolower($1) == "transfer-encoding" { gsub("\\r", "", $2); print tolower($2) }' "$headers_file")"
[[ -z "$transfer_encoding" || "$transfer_encoding" == chunked ]] || fail "unexpected transfer encoding: $transfer_encoding"

docker update --cpus 2 openappsec-agent >/dev/null
cpu_boosted=false
printf '%s\n' "$backup_dir" > "$stack_root/last-prevent-backup"
success=true
printf 'prevent=pass mode=Prevent severity=Medium action=High response=403 body=10\n'
