name: Run whitelisted SQL on liderra.ru on: workflow_dispatch: inputs: sql: description: 'SQL query (SELECT only by default; UPDATE/DELETE need confirm_mutating=true)' required: true type: string confirm_mutating: description: 'Подтверждаю UPDATE/DELETE на проде' required: false default: false type: boolean jobs: run: runs-on: ubuntu-latest timeout-minutes: 10 env: LIDERRA_HOST: 111.88.246.137 LIDERRA_USER: ubuntu SQL: ${{ github.event.inputs.sql }} CONFIRM_MUT: ${{ github.event.inputs.confirm_mutating }} steps: - name: Whitelist check run: | set -euo pipefail SQL_LOWER=$(echo "$SQL" | tr '[:upper:]' '[:lower:]' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') # Reject multi-statement SQL — `;` would let SELECT-prefixed payloads # smuggle UPDATE/DELETE past READ_RE without confirm_mutating=true. # Trailing single `;` is also rejected for symmetry (use no trailing `;`). if [[ "$SQL_LOWER" == *";"* ]]; then echo "::error::Multi-statement SQL is not allowed (no semicolons)." exit 1 fi # Allow: SELECT / WITH (CTE) / \d / EXPLAIN READ_RE='^(select |with |explain |\\d|\\df|\\di|\\dt)' # Mutating allowed if confirm=true: targeted UPDATE/DELETE on specific tables MUTATING_RE='^(update supplier_leads|update failed_webhook_jobs|update scheduler_heartbeats|delete from failed_webhook_jobs|delete from incidents_log) ' if [[ "$SQL_LOWER" =~ $READ_RE ]]; then echo "::notice::SELECT/read-only — allowed." exit 0 fi if [[ "$SQL_LOWER" =~ $MUTATING_RE ]]; then if [[ "$CONFIRM_MUT" != "true" ]]; then echo "::error::Mutating SQL requires confirm_mutating=true." exit 1 fi echo "::warning::Mutating SQL authorized." exit 0 fi echo "::error::SQL not in whitelist: $SQL_LOWER" exit 1 - name: Setup SSH key run: | mkdir -p ~/.ssh echo "${{ secrets.LIDERRA_SSH_KEY }}" > ~/.ssh/liderra_deploy chmod 600 ~/.ssh/liderra_deploy ssh-keyscan -H ${{ env.LIDERRA_HOST }} >> ~/.ssh/known_hosts 2>/dev/null - name: Run on prod run: | set -o pipefail SQL_B64=$(printf '%s' "$SQL" | base64 -w0) ssh -i ~/.ssh/liderra_deploy ${{ env.LIDERRA_USER }}@${{ env.LIDERRA_HOST }} \ "SQL_B64='$SQL_B64' bash -s" <<'REMOTE' | tee /tmp/sql.log SQL=$(echo "$SQL_B64" | base64 -d) echo "=== Running on $(hostname) at $(date -u) ===" echo "SQL: $SQL" echo sudo -u postgres psql -d liderra -c "$SQL" RC=$? echo echo "=== Exit code: $RC ===" exit $RC REMOTE - name: Summary if: always() run: | { echo "## SQL on prod" echo echo '```sql' echo "$SQL" echo '```' echo echo '```' cat /tmp/sql.log 2>/dev/null echo '```' } >> "$GITHUB_STEP_SUMMARY" - name: Cleanup if: always() run: rm -f ~/.ssh/liderra_deploy