diff --git a/.gitea/workflows/nightly.yml b/.gitea/workflows/nightly.yml index e933f327..14a962ed 100644 --- a/.gitea/workflows/nightly.yml +++ b/.gitea/workflows/nightly.yml @@ -252,20 +252,20 @@ jobs: URL="https://$HOST" HOST_IP=$(awk 'NR>1 && $2=="00000000"{h=$3;printf "%d.%d.%d.%d\n",strtonum("0x"substr(h,7,2)),strtonum("0x"substr(h,5,2)),strtonum("0x"substr(h,3,2)),strtonum("0x"substr(h,1,2));exit}' /proc/net/route) [ -n "$HOST_IP" ] || { echo "ERROR: could not detect Docker bridge gateway via /proc/net/route"; exit 1; } - RESOLVE="--resolve $HOST:443:$HOST_IP" + RESOLVE=(--resolve "$HOST:443:$HOST_IP") echo "Smoke test: $URL (pinned to $HOST_IP via bridge gateway)" - curl -fsS "$RESOLVE" --max-time 10 "$URL/login" -o /dev/null + curl -fsS "${RESOLVE[@]}" --max-time 10 "$URL/login" -o /dev/null # Pin the preload-list-eligible HSTS value, not just header presence: # a degraded `max-age=1` or a dropped `includeSubDomains; preload` must # fail this check rather than pass it silently. - curl -fsS "$RESOLVE" --max-time 10 -I "$URL/" \ + curl -fsS "${RESOLVE[@]}" --max-time 10 -I "$URL/" \ | grep -Eqi 'strict-transport-security:[[:space:]]*max-age=31536000.*includeSubDomains.*preload' # Permissions-Policy denies APIs the app does not use (camera, # microphone, geolocation). A regression that loosens or drops the # header now fails the smoke step. - curl -fsS "$RESOLVE" --max-time 10 -I "$URL/" \ + curl -fsS "${RESOLVE[@]}" --max-time 10 -I "$URL/" \ | grep -Eqi 'permissions-policy:[[:space:]]*camera=\(\),[[:space:]]*microphone=\(\),[[:space:]]*geolocation=\(\)' - status=$(curl -s "$RESOLVE" -o /dev/null -w "%{http_code}" --max-time 10 "$URL/actuator/health") + status=$(curl -s "${RESOLVE[@]}" -o /dev/null -w "%{http_code}" --max-time 10 "$URL/actuator/health") [ "$status" = "404" ] || { echo "expected 404 from /actuator/health, got $status"; exit 1; } echo "All smoke checks passed" diff --git a/.gitea/workflows/release.yml b/.gitea/workflows/release.yml index 99d0a0f5..cf30bf83 100644 --- a/.gitea/workflows/release.yml +++ b/.gitea/workflows/release.yml @@ -181,28 +181,31 @@ jobs: - name: Smoke test deployed environment # See nightly.yml — same three checks, against the prod vhost. - # --resolve pins to the bridge gateway IP (the host), not 127.0.0.1 - # — see nightly.yml for the full network topology explanation. + # --resolve stored as a Bash array so "${RESOLVE[@]}" expands to two + # separate arguments; a quoted string would pass the flag and its value + # as one token and curl would reject it as an unknown option. + # Gateway detection via /proc/net/route — no iproute2 dependency. + # See nightly.yml for the full network topology explanation. run: | set -e HOST="archiv.raddatz.cloud" URL="https://$HOST" - HOST_IP=$(ip route show default | awk '/default/ {print $3}') - [ -n "$HOST_IP" ] || { echo "ERROR: could not detect Docker bridge gateway via 'ip route'"; exit 1; } - RESOLVE="--resolve $HOST:443:$HOST_IP" + HOST_IP=$(awk 'NR>1 && $2=="00000000"{h=$3;printf "%d.%d.%d.%d\n",strtonum("0x"substr(h,7,2)),strtonum("0x"substr(h,5,2)),strtonum("0x"substr(h,3,2)),strtonum("0x"substr(h,1,2));exit}' /proc/net/route) + [ -n "$HOST_IP" ] || { echo "ERROR: could not detect Docker bridge gateway via /proc/net/route"; exit 1; } + RESOLVE=(--resolve "$HOST:443:$HOST_IP") echo "Smoke test: $URL (pinned to $HOST_IP via bridge gateway)" - curl -fsS "$RESOLVE" --max-time 10 "$URL/login" -o /dev/null + curl -fsS "${RESOLVE[@]}" --max-time 10 "$URL/login" -o /dev/null # Pin the preload-list-eligible HSTS value, not just header presence: # a degraded `max-age=1` or a dropped `includeSubDomains; preload` must # fail this check rather than pass it silently. - curl -fsS "$RESOLVE" --max-time 10 -I "$URL/" \ + curl -fsS "${RESOLVE[@]}" --max-time 10 -I "$URL/" \ | grep -Eqi 'strict-transport-security:[[:space:]]*max-age=31536000.*includeSubDomains.*preload' # Permissions-Policy denies APIs the app does not use (camera, # microphone, geolocation). A regression that loosens or drops the # header now fails the smoke step. - curl -fsS "$RESOLVE" --max-time 10 -I "$URL/" \ + curl -fsS "${RESOLVE[@]}" --max-time 10 -I "$URL/" \ | grep -Eqi 'permissions-policy:[[:space:]]*camera=\(\),[[:space:]]*microphone=\(\),[[:space:]]*geolocation=\(\)' - status=$(curl -s "$RESOLVE" -o /dev/null -w "%{http_code}" --max-time 10 "$URL/actuator/health") + status=$(curl -s "${RESOLVE[@]}" -o /dev/null -w "%{http_code}" --max-time 10 "$URL/actuator/health") [ "$status" = "404" ] || { echo "expected 404 from /actuator/health, got $status"; exit 1; } echo "All smoke checks passed"