HEX
Server: nginx/1.29.3
System: Linux 11979.bigscoots-wpo.com 6.8.0-88-generic #89-Ubuntu SMP PREEMPT_DYNAMIC Sat Oct 11 01:02:46 UTC 2025 x86_64
User: nginx (1068)
PHP: 7.4.33
Disabled: exec,system,passthru,shell_exec,proc_open,proc_close,popen,show_source,cmd# Do not modify this line # 1684243876
Upload Files
File: //bigscoots/wpo/cloudflare/challenge_wplogin.sh
#!/bin/bash
# Usage: ./add_wplogin_challenge.sh <email> <api_key> <zone_id> [extra_paths]
#
# extra_paths: optional comma-separated list of additional paths to challenge
#
# Examples:
#   ./add_wplogin_challenge.sh email apikey zoneid
#   ./add_wplogin_challenge.sh email apikey zoneid "my-account"
#   ./add_wplogin_challenge.sh email apikey zoneid "my-account,wp-admin,checkout"

set -euo pipefail

EMAIL="$1"
API_KEY="$2"
ZONE_ID="$3"
EXTRA_PATHS="${4:-}"

API_BASE="https://api.cloudflare.com/client/v4"
ENDPOINT="$API_BASE/zones/$ZONE_ID/rulesets/phases/http_request_firewall_custom/entrypoint"

# ── Build expression from paths ───────────────────────────────────────────────
# Always starts with wp-login.php, then ORs in any extra paths
PATHS="wp-login.php"
if [[ -n "$EXTRA_PATHS" ]]; then
  PATHS="$PATHS,$EXTRA_PATHS"
fi

# Build the path conditions as OR chain
PATH_CONDITIONS=""
IFS=',' read -ra PATH_LIST <<< "$PATHS"
for CF_PATH in "${PATH_LIST[@]}"; do
  CF_PATH="${CF_PATH#"${CF_PATH%%[![:space:]]*}"}"  # trim leading whitespace
  CF_PATH="${CF_PATH%"${CF_PATH##*[![:space:]]}"}"  # trim trailing whitespace
  CONDITION="http.request.uri.path contains \"$CF_PATH\""
  if [[ -z "$PATH_CONDITIONS" ]]; then
    PATH_CONDITIONS="$CONDITION"
  else
    PATH_CONDITIONS="$PATH_CONDITIONS or $CONDITION"
  fi
done

# Wrap in parens with logout exclusion
EXPRESSION="($PATH_CONDITIONS) and not http.request.uri.query contains \"action=logout\""

# Build description
if [[ ${#PATH_LIST[@]} -gt 1 ]]; then
  DESCRIPTION="Challenge $(IFS=', '; echo "${PATH_LIST[*]}")"
else
  DESCRIPTION="Challenge wp-login.php"
fi

echo "Expression: $EXPRESSION"

# Build rule JSON safely with jq
NEW_RULE=$(jq -n \
  --arg action "managed_challenge" \
  --arg description "$DESCRIPTION" \
  --arg expression "$EXPRESSION" \
  '{action: $action, description: $description, enabled: true, expression: $expression}')

# ── Step 1: GET current ruleset ───────────────────────────────────────────────
echo "Fetching current rules for zone $ZONE_ID..."

RESPONSE=$(curl -s "$ENDPOINT" \
  -H "X-Auth-Email: $EMAIL" \
  -H "X-Auth-Key: $API_KEY")

SUCCESS=$(echo "$RESPONSE" | jq -r '.success')

if [[ "$SUCCESS" != "true" ]]; then
  # Zone may not have any custom rules yet — that's fine, start with empty array
  ERROR_CODE=$(echo "$RESPONSE" | jq -r '.errors[0].code // ""')
  if [[ "$ERROR_CODE" == "10001" ]] || echo "$RESPONSE" | jq -e '.result == null' > /dev/null 2>&1; then
    echo "No existing custom ruleset found — will create fresh."
    RULES="[]"
  else
    echo "Error fetching ruleset:"
    echo "$RESPONSE" | jq '.errors'
    exit 1
  fi
else
  RULES=$(echo "$RESPONSE" | jq '.result.rules // []')
fi

# ── Step 2: Check if wp-login rule already exists ────────────────────────────
EXISTING_ACTION=$(echo "$RULES" | jq -r '.[] | select(.expression | contains("wp-login.php")) | .action' | head -1)

if [[ "$EXISTING_ACTION" == "managed_challenge" ]]; then
  EXISTING_EXPRESSION=$(echo "$RULES" | jq -r '.[] | select(.expression | contains("wp-login.php")) | .expression' | head -1)
  if [[ "$EXISTING_EXPRESSION" == "$EXPRESSION" ]]; then
    echo "✓ Rule already exists with the correct expression. Nothing to do."
    exit 0
  else
    echo "⚠ A managed_challenge rule for wp-login.php exists but with a different expression."
    echo "  Existing: $EXISTING_EXPRESSION"
    echo "  Updating to: $EXPRESSION"
    RULES=$(echo "$RULES" | jq --arg expr "wp-login.php" --argjson new "$NEW_RULE" '
      map(if (.expression | contains($expr)) then $new + {id: .id, ref: .ref} else . end)
    ')
  fi
elif [[ -n "$EXISTING_ACTION" ]]; then
  echo "⚠ A wp-login.php rule already exists with action: $EXISTING_ACTION"
  echo "  Updating it to managed_challenge..."
  RULES=$(echo "$RULES" | jq --arg expr "wp-login.php" --argjson new "$NEW_RULE" '
    map(if (.expression | contains($expr)) then $new + {id: .id, ref: .ref} else . end)
  ')
else
  # ── Step 3: Insert after last skip rule, or at top if none ─────────────────
  LAST_SKIP_INDEX=$(echo "$RULES" | jq '[to_entries[] | select(.value.action == "skip")] | last | .key // -1')

  if [[ "$LAST_SKIP_INDEX" -ge 0 ]]; then
    INSERT_AT=$((LAST_SKIP_INDEX + 1))
    echo "Inserting rule at position $INSERT_AT (after last skip rule)..."
  else
    INSERT_AT=0
    echo "No skip rules found — inserting rule at top..."
  fi

  RULES=$(echo "$RULES" | jq --argjson new "$NEW_RULE" --argjson idx "$INSERT_AT" '
    .[:$idx] + [$new] + .[$idx:]
  ')
fi

# ── Step 4: PUT the updated ruleset back ─────────────────────────────────────
echo "Applying updated ruleset..."

PUT_RESPONSE=$(curl -s -X PUT "$ENDPOINT" \
  -H "X-Auth-Email: $EMAIL" \
  -H "X-Auth-Key: $API_KEY" \
  -H "Content-Type: application/json" \
  -d "{\"rules\": $RULES}")

PUT_SUCCESS=$(echo "$PUT_RESPONSE" | jq -r '.success')

if [[ "$PUT_SUCCESS" == "true" ]]; then
  echo "✓ Done! Final rule order:"
  echo "$PUT_RESPONSE" | jq '.result.rules[] | {position: (.id), action, description, expression}'
else
  echo "✗ Failed to update ruleset:"
  echo "$PUT_RESPONSE" | jq '.errors'
  exit 1
fi